Make Eve surface documents canonical
This commit is contained in:
parent
39a994a879
commit
f909b274fa
4 changed files with 596 additions and 37 deletions
377
packages/org.gamecult.eve.surface/Runtime/EveSurfaceBuilder.cs
Normal file
377
packages/org.gamecult.eve.surface/Runtime/EveSurfaceBuilder.cs
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameCult.Mesh;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Eve.Surface
|
||||
{
|
||||
/// <summary>
|
||||
/// Fluent CultUI/Eve surface builder. Eve owns the surface document; CultMesh only supplies typed binding descriptors.
|
||||
/// </summary>
|
||||
public sealed class EveSurfaceBuilder
|
||||
{
|
||||
private readonly string _surfaceId;
|
||||
private readonly List<EveSurfaceComponent> _children = new();
|
||||
private readonly List<EveStyleToken> _styles = new();
|
||||
private readonly List<EveCommandTemplate> _commands = new();
|
||||
private long _version = 1;
|
||||
private string _providerId = "gamecult";
|
||||
private string _providerKind = "cultui.surface";
|
||||
private string _title = "";
|
||||
private string _updatedAtUtc = "";
|
||||
|
||||
public EveSurfaceBuilder(string surfaceId)
|
||||
{
|
||||
_surfaceId = RequireNonEmpty(surfaceId, nameof(surfaceId));
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Provider(string providerId, string providerKind)
|
||||
{
|
||||
_providerId = providerId ?? "";
|
||||
_providerKind = providerKind ?? "";
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Title(string title)
|
||||
{
|
||||
_title = title ?? "";
|
||||
_children.Add(Text($"{_surfaceId}.title", _title, "text.title"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder TitleSubtitle(string title, string subtitle)
|
||||
{
|
||||
_title = string.IsNullOrWhiteSpace(subtitle) ? title ?? "" : $"{title} {subtitle}";
|
||||
_children.Add(Text($"{_surfaceId}.title", title ?? "", "text.title"));
|
||||
_children.Add(Text($"{_surfaceId}.subtitle", subtitle ?? "", "text.subtitle"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Text(string text, string? id = null)
|
||||
{
|
||||
_children.Add(Text(id ?? $"{_surfaceId}.text.{_children.Count}", text, "text"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Button(string label, string command)
|
||||
{
|
||||
var operation = CultMesh.OperationBinding(command, label);
|
||||
_commands.Add(new EveCommandTemplate(operation));
|
||||
_children.Add(ButtonComponent($"{_surfaceId}.button.{Slug(label)}", label, operation));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Button(string label, CultMeshOperationBindingDescriptor operation)
|
||||
{
|
||||
if (operation == null) throw new ArgumentNullException(nameof(operation));
|
||||
_commands.Add(new EveCommandTemplate(operation));
|
||||
_children.Add(ButtonComponent($"{_surfaceId}.button.{Slug(label)}", label, operation));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder ButtonColumn(string id, Action<EveSurfaceGroupBuilder> build)
|
||||
{
|
||||
return Group(id, "column", build);
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder ButtonRow(string id, Action<EveSurfaceGroupBuilder> build)
|
||||
{
|
||||
return Group(id, "row", build);
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Form(string id, Action<EveSurfaceFormBuilder> build)
|
||||
{
|
||||
if (build == null) throw new ArgumentNullException(nameof(build));
|
||||
var form = new EveSurfaceFormBuilder(id, AddCommand);
|
||||
build(form);
|
||||
_children.Add(form.Build());
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder EmbeddedDocument(
|
||||
string slotId,
|
||||
string documentId,
|
||||
string schemaId,
|
||||
string presentationKind,
|
||||
CultMeshRouteHint? routeHint = null)
|
||||
{
|
||||
_children.Add(new EveSurfaceComponent(
|
||||
$"{_surfaceId}.slot.{Slug(slotId)}",
|
||||
"surface.slot",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["slotId"] = slotId ?? "",
|
||||
["documentId"] = documentId ?? "",
|
||||
["schemaId"] = schemaId ?? "",
|
||||
["presentationKind"] = presentationKind ?? ""
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>(),
|
||||
Array.Empty<CultMeshStateBindingDescriptor>(),
|
||||
new[]
|
||||
{
|
||||
new EveEmbeddedDocumentSlot(
|
||||
slotId ?? "",
|
||||
documentId ?? "",
|
||||
schemaId ?? "",
|
||||
presentationKind ?? "",
|
||||
routeHint)
|
||||
}));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Style(string name, string value)
|
||||
{
|
||||
_styles.Add(new EveStyleToken(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder Version(long version)
|
||||
{
|
||||
_version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceBuilder UpdatedAtUtc(string updatedAtUtc)
|
||||
{
|
||||
_updatedAtUtc = updatedAtUtc ?? "";
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceDocument Build()
|
||||
{
|
||||
return new EveSurfaceDocument(
|
||||
_providerId,
|
||||
_providerKind,
|
||||
_title,
|
||||
_version,
|
||||
string.IsNullOrWhiteSpace(_updatedAtUtc) ? DateTime.UtcNow.ToString("O") : _updatedAtUtc,
|
||||
new EveSurfaceTree(
|
||||
_surfaceId,
|
||||
new EveSurfaceComponent(
|
||||
$"{_surfaceId}.root",
|
||||
"surface",
|
||||
EmptyProps(),
|
||||
_children.ToArray()),
|
||||
_styles.ToArray()),
|
||||
_commands.ToArray());
|
||||
}
|
||||
|
||||
private EveSurfaceBuilder Group(string id, string kind, Action<EveSurfaceGroupBuilder> build)
|
||||
{
|
||||
if (build == null) throw new ArgumentNullException(nameof(build));
|
||||
var group = new EveSurfaceGroupBuilder(id, kind, AddCommand);
|
||||
build(group);
|
||||
_children.Add(group.Build());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void AddCommand(EveCommandTemplate command)
|
||||
{
|
||||
if (command != null)
|
||||
_commands.Add(command);
|
||||
}
|
||||
|
||||
internal static EveSurfaceComponent Text(string id, string value, string kind)
|
||||
{
|
||||
return new EveSurfaceComponent(
|
||||
id,
|
||||
kind,
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["value"] = value ?? ""
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>());
|
||||
}
|
||||
|
||||
internal static EveSurfaceComponent ButtonComponent(
|
||||
string id,
|
||||
string label,
|
||||
CultMeshOperationBindingDescriptor operation)
|
||||
{
|
||||
return new EveSurfaceComponent(
|
||||
id,
|
||||
"control.button",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["label"] = label ?? operation.Label,
|
||||
["command"] = operation.OperationId,
|
||||
["operationId"] = operation.OperationId,
|
||||
["schemaId"] = operation.SchemaId
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>());
|
||||
}
|
||||
|
||||
internal static Dictionary<string, string> EmptyProps()
|
||||
{
|
||||
return new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
internal static string Slug(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return "unnamed";
|
||||
|
||||
var chars = value
|
||||
.Select(character => char.IsLetterOrDigit(character) ? char.ToLowerInvariant(character) : '.')
|
||||
.ToArray();
|
||||
var slug = new string(chars).Trim('.');
|
||||
while (slug.Contains("..", StringComparison.Ordinal))
|
||||
slug = slug.Replace("..", ".", StringComparison.Ordinal);
|
||||
return string.IsNullOrWhiteSpace(slug) ? "unnamed" : slug;
|
||||
}
|
||||
|
||||
private static string RequireNonEmpty(string value, string paramName)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(value)
|
||||
? throw new ArgumentException("Value must be non-empty.", paramName)
|
||||
: value;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EveSurfaceGroupBuilder
|
||||
{
|
||||
private readonly string _id;
|
||||
private readonly string _kind;
|
||||
private readonly Action<EveCommandTemplate> _addCommand;
|
||||
private readonly List<EveSurfaceComponent> _children = new();
|
||||
|
||||
internal EveSurfaceGroupBuilder(
|
||||
string id,
|
||||
string kind,
|
||||
Action<EveCommandTemplate> addCommand)
|
||||
{
|
||||
_id = id ?? "";
|
||||
_kind = kind ?? "column";
|
||||
_addCommand = addCommand ?? throw new ArgumentNullException(nameof(addCommand));
|
||||
}
|
||||
|
||||
public EveSurfaceGroupBuilder Button(string label, string command)
|
||||
{
|
||||
var operation = CultMesh.OperationBinding(command, label);
|
||||
_addCommand(new EveCommandTemplate(operation));
|
||||
_children.Add(EveSurfaceBuilder.ButtonComponent($"{_id}.button.{EveSurfaceBuilder.Slug(label)}", label, operation));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceGroupBuilder Button(string label, CultMeshOperationBindingDescriptor operation)
|
||||
{
|
||||
if (operation == null) throw new ArgumentNullException(nameof(operation));
|
||||
_addCommand(new EveCommandTemplate(operation));
|
||||
_children.Add(EveSurfaceBuilder.ButtonComponent($"{_id}.button.{EveSurfaceBuilder.Slug(label)}", label, operation));
|
||||
return this;
|
||||
}
|
||||
|
||||
internal EveSurfaceComponent Build()
|
||||
{
|
||||
return new EveSurfaceComponent(_id, _kind, EveSurfaceBuilder.EmptyProps(), _children.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EveSurfaceFormBuilder
|
||||
{
|
||||
private readonly string _id;
|
||||
private readonly Action<EveCommandTemplate> _addCommand;
|
||||
private readonly List<EveSurfaceComponent> _children = new();
|
||||
|
||||
internal EveSurfaceFormBuilder(string id, Action<EveCommandTemplate> addCommand)
|
||||
{
|
||||
_id = id ?? "";
|
||||
_addCommand = addCommand ?? throw new ArgumentNullException(nameof(addCommand));
|
||||
}
|
||||
|
||||
public EveSurfaceFormBuilder Text(
|
||||
string label,
|
||||
string value,
|
||||
CultMeshOperationBindingDescriptor operation,
|
||||
CultMeshStateBindingDescriptor? binding = null)
|
||||
{
|
||||
if (operation == null) throw new ArgumentNullException(nameof(operation));
|
||||
_addCommand(new EveCommandTemplate(operation));
|
||||
_children.Add(Control(
|
||||
"control.text",
|
||||
label,
|
||||
value,
|
||||
operation,
|
||||
binding));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceFormBuilder Toggle(
|
||||
string label,
|
||||
bool value,
|
||||
CultMeshOperationBindingDescriptor operation,
|
||||
CultMeshStateBindingDescriptor? binding = null)
|
||||
{
|
||||
if (operation == null) throw new ArgumentNullException(nameof(operation));
|
||||
_addCommand(new EveCommandTemplate(operation));
|
||||
_children.Add(Control(
|
||||
"control.toggle",
|
||||
label,
|
||||
value ? "true" : "false",
|
||||
operation,
|
||||
binding));
|
||||
return this;
|
||||
}
|
||||
|
||||
public EveSurfaceFormBuilder Metric(
|
||||
string label,
|
||||
string value,
|
||||
CultMeshStateBindingDescriptor? binding = null)
|
||||
{
|
||||
var bindings = binding == null
|
||||
? Array.Empty<CultMeshStateBindingDescriptor>()
|
||||
: new[] { binding };
|
||||
_children.Add(new EveSurfaceComponent(
|
||||
$"{_id}.metric.{EveSurfaceBuilder.Slug(label)}",
|
||||
"metric",
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["label"] = label ?? "",
|
||||
["value"] = value ?? ""
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>(),
|
||||
bindings));
|
||||
return this;
|
||||
}
|
||||
|
||||
internal EveSurfaceComponent Build()
|
||||
{
|
||||
return new EveSurfaceComponent(_id, "form", EveSurfaceBuilder.EmptyProps(), _children.ToArray());
|
||||
}
|
||||
|
||||
private EveSurfaceComponent Control(
|
||||
string kind,
|
||||
string label,
|
||||
string value,
|
||||
CultMeshOperationBindingDescriptor operation,
|
||||
CultMeshStateBindingDescriptor? binding)
|
||||
{
|
||||
var bindings = binding == null
|
||||
? Array.Empty<CultMeshStateBindingDescriptor>()
|
||||
: new[] { binding };
|
||||
return new EveSurfaceComponent(
|
||||
$"{_id}.{EveSurfaceBuilder.Slug(label)}",
|
||||
kind,
|
||||
new Dictionary<string, string>(StringComparer.Ordinal)
|
||||
{
|
||||
["label"] = label ?? "",
|
||||
["value"] = value ?? "",
|
||||
["command"] = operation.OperationId,
|
||||
["operationId"] = operation.OperationId,
|
||||
["schemaId"] = operation.SchemaId
|
||||
},
|
||||
Array.Empty<EveSurfaceComponent>(),
|
||||
bindings);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EveSurface
|
||||
{
|
||||
public static EveSurfaceBuilder Create(string surfaceId)
|
||||
{
|
||||
return new EveSurfaceBuilder(surfaceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GameCult.Caching;
|
||||
using GameCult.Mesh;
|
||||
using MessagePack;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Eve.Surface
|
||||
{
|
||||
[CultDocument("gamecult.eve.surface", "gamecult.eve.surface.v1")]
|
||||
[MessagePackObject]
|
||||
public sealed class EveSurfaceDocument
|
||||
{
|
||||
public const string DefaultType = "surface-state";
|
||||
public const string SchemaId = "gamecult.eve.surface.v1";
|
||||
|
||||
public EveSurfaceDocument(
|
||||
string providerId,
|
||||
string providerKind,
|
||||
string title,
|
||||
long version,
|
||||
string updatedAtUtc,
|
||||
EveSurfaceTree surface,
|
||||
IReadOnlyList<EveCommandTemplate> commands)
|
||||
: this(DefaultType, SchemaId, providerId, providerKind, title, version, updatedAtUtc, surface, commands)
|
||||
{
|
||||
}
|
||||
|
||||
[SerializationConstructor]
|
||||
public EveSurfaceDocument(
|
||||
string type,
|
||||
string schema,
|
||||
|
|
@ -18,52 +39,67 @@ namespace GameCult.Eve.Surface
|
|||
EveSurfaceTree surface,
|
||||
IReadOnlyList<EveCommandTemplate> commands)
|
||||
{
|
||||
Type = type;
|
||||
Schema = schema;
|
||||
ProviderId = providerId;
|
||||
ProviderKind = providerKind;
|
||||
Title = title;
|
||||
Type = string.IsNullOrWhiteSpace(type) ? DefaultType : type;
|
||||
Schema = string.IsNullOrWhiteSpace(schema) ? SchemaId : schema;
|
||||
ProviderId = providerId ?? "";
|
||||
ProviderKind = providerKind ?? "";
|
||||
Title = title ?? "";
|
||||
Version = version;
|
||||
UpdatedAtUtc = updatedAtUtc;
|
||||
Surface = surface;
|
||||
Commands = commands;
|
||||
UpdatedAtUtc = updatedAtUtc ?? "";
|
||||
Surface = surface ?? throw new ArgumentNullException(nameof(surface));
|
||||
Commands = commands ?? Array.Empty<EveCommandTemplate>();
|
||||
}
|
||||
|
||||
[Key(0)]
|
||||
public string Type { get; }
|
||||
|
||||
[Key(1)]
|
||||
public string Schema { get; }
|
||||
|
||||
[Key(2)]
|
||||
public string ProviderId { get; }
|
||||
|
||||
[Key(3)]
|
||||
public string ProviderKind { get; }
|
||||
|
||||
[Key(4)]
|
||||
public string Title { get; }
|
||||
|
||||
[Key(5)]
|
||||
public long Version { get; }
|
||||
|
||||
[Key(6)]
|
||||
public string UpdatedAtUtc { get; }
|
||||
|
||||
[Key(7)]
|
||||
public EveSurfaceTree Surface { get; }
|
||||
|
||||
[Key(8)]
|
||||
public IReadOnlyList<EveCommandTemplate> Commands { get; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class EveSurfaceTree
|
||||
{
|
||||
[SerializationConstructor]
|
||||
public EveSurfaceTree(string id, EveSurfaceComponent root, IReadOnlyList<EveStyleToken> styles)
|
||||
{
|
||||
Id = id;
|
||||
Root = root;
|
||||
Styles = styles;
|
||||
Id = id ?? "";
|
||||
Root = root ?? throw new ArgumentNullException(nameof(root));
|
||||
Styles = styles ?? Array.Empty<EveStyleToken>();
|
||||
}
|
||||
|
||||
[Key(0)]
|
||||
public string Id { get; }
|
||||
|
||||
[Key(1)]
|
||||
public EveSurfaceComponent Root { get; }
|
||||
|
||||
[Key(2)]
|
||||
public IReadOnlyList<EveStyleToken> Styles { get; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class EveSurfaceComponent
|
||||
{
|
||||
public EveSurfaceComponent(
|
||||
|
|
@ -71,54 +107,156 @@ namespace GameCult.Eve.Surface
|
|||
string kind,
|
||||
IReadOnlyDictionary<string, string> props,
|
||||
IReadOnlyList<EveSurfaceComponent> children)
|
||||
: this(id, kind, props, children, StateBindingsFromProps(props))
|
||||
{
|
||||
Id = id;
|
||||
Kind = kind;
|
||||
Props = props;
|
||||
Children = children;
|
||||
}
|
||||
|
||||
public EveSurfaceComponent(
|
||||
string id,
|
||||
string kind,
|
||||
IReadOnlyDictionary<string, string> props,
|
||||
IReadOnlyList<EveSurfaceComponent> children,
|
||||
IReadOnlyList<CultMeshStateBindingDescriptor> stateBindings)
|
||||
: this(id, kind, props, children, stateBindings, Array.Empty<EveEmbeddedDocumentSlot>())
|
||||
{
|
||||
}
|
||||
|
||||
[SerializationConstructor]
|
||||
public EveSurfaceComponent(
|
||||
string id,
|
||||
string kind,
|
||||
IReadOnlyDictionary<string, string> props,
|
||||
IReadOnlyList<EveSurfaceComponent> children,
|
||||
IReadOnlyList<CultMeshStateBindingDescriptor> stateBindings,
|
||||
IReadOnlyList<EveEmbeddedDocumentSlot> embeddedDocuments)
|
||||
{
|
||||
Id = id ?? "";
|
||||
Kind = kind ?? "";
|
||||
Props = props ?? new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
Children = children ?? Array.Empty<EveSurfaceComponent>();
|
||||
StateBindings = stateBindings ?? Array.Empty<CultMeshStateBindingDescriptor>();
|
||||
EmbeddedDocuments = embeddedDocuments ?? Array.Empty<EveEmbeddedDocumentSlot>();
|
||||
}
|
||||
|
||||
[Key(0)]
|
||||
public string Id { get; }
|
||||
|
||||
[Key(1)]
|
||||
public string Kind { get; }
|
||||
|
||||
[Key(2)]
|
||||
public IReadOnlyDictionary<string, string> Props { get; }
|
||||
|
||||
[Key(3)]
|
||||
public IReadOnlyList<EveSurfaceComponent> Children { get; }
|
||||
|
||||
[Key(4)]
|
||||
public IReadOnlyList<CultMeshStateBindingDescriptor> StateBindings { get; }
|
||||
|
||||
[Key(5)]
|
||||
public IReadOnlyList<EveEmbeddedDocumentSlot> EmbeddedDocuments { get; }
|
||||
|
||||
public string GetProp(string key, string fallback = "")
|
||||
{
|
||||
return Props.TryGetValue(key, out var value) ? value : fallback;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CultMeshStateBindingDescriptor> StateBindingsFromProps(
|
||||
IReadOnlyDictionary<string, string>? props)
|
||||
{
|
||||
if (props == null || props.Count == 0)
|
||||
return Array.Empty<CultMeshStateBindingDescriptor>();
|
||||
|
||||
var bindings = new List<CultMeshStateBindingDescriptor>();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(prop.Value) ||
|
||||
!prop.Key.EndsWith("PointerId", StringComparison.Ordinal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetProp = prop.Key.Substring(0, prop.Key.Length - "PointerId".Length);
|
||||
if (targetProp.Length == 0)
|
||||
targetProp = "value";
|
||||
bindings.Add(new CultMeshStateBindingDescriptor(targetProp, prop.Value));
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EveStyleToken
|
||||
[MessagePackObject]
|
||||
public sealed class EveEmbeddedDocumentSlot
|
||||
{
|
||||
public EveStyleToken(string name, string value)
|
||||
[SerializationConstructor]
|
||||
public EveEmbeddedDocumentSlot(
|
||||
string slotId,
|
||||
string documentId,
|
||||
string schemaId,
|
||||
string presentationKind,
|
||||
CultMeshRouteHint? routeHint = null)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
SlotId = slotId ?? "";
|
||||
DocumentId = documentId ?? "";
|
||||
SchemaId = schemaId ?? "";
|
||||
PresentationKind = presentationKind ?? "";
|
||||
RouteHint = routeHint ?? CultMeshRouteHint.Automatic;
|
||||
}
|
||||
|
||||
[Key(0)]
|
||||
public string SlotId { get; }
|
||||
|
||||
[Key(1)]
|
||||
public string DocumentId { get; }
|
||||
|
||||
[Key(2)]
|
||||
public string SchemaId { get; }
|
||||
|
||||
[Key(3)]
|
||||
public string PresentationKind { get; }
|
||||
|
||||
[Key(4)]
|
||||
public CultMeshRouteHint RouteHint { get; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class EveStyleToken
|
||||
{
|
||||
[SerializationConstructor]
|
||||
public EveStyleToken(string name, string value)
|
||||
{
|
||||
Name = name ?? "";
|
||||
Value = value ?? "";
|
||||
}
|
||||
|
||||
[Key(0)]
|
||||
public string Name { get; }
|
||||
|
||||
[Key(1)]
|
||||
public string Value { get; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
public sealed class EveCommandTemplate
|
||||
{
|
||||
public EveCommandTemplate(string command, string label, string transport)
|
||||
[SerializationConstructor]
|
||||
public EveCommandTemplate(CultMeshOperationBindingDescriptor operation)
|
||||
{
|
||||
Command = command;
|
||||
Label = label;
|
||||
Transport = transport;
|
||||
Operation = operation ?? throw new ArgumentNullException(nameof(operation));
|
||||
}
|
||||
|
||||
public string Command { get; }
|
||||
[Key(0)]
|
||||
public CultMeshOperationBindingDescriptor Operation { get; }
|
||||
|
||||
public string Label { get; }
|
||||
[IgnoreMember]
|
||||
public string Command => Operation.OperationId;
|
||||
|
||||
public string Transport { get; }
|
||||
[IgnoreMember]
|
||||
public string Label => Operation.Label;
|
||||
|
||||
[IgnoreMember]
|
||||
public string Transport => Operation.RouteHint.Description ?? "";
|
||||
}
|
||||
|
||||
public sealed class EveSurfaceCommandRequest
|
||||
|
|
@ -126,15 +264,15 @@ namespace GameCult.Eve.Surface
|
|||
public EveSurfaceCommandRequest(
|
||||
string providerId,
|
||||
string surfaceId,
|
||||
string command,
|
||||
IReadOnlyDictionary<string, string> payload,
|
||||
CultMeshOperationInvocationDescriptor operation,
|
||||
CultMeshOperationPayload payload,
|
||||
DateTimeOffset issuedAt,
|
||||
string clientId)
|
||||
{
|
||||
ProviderId = providerId;
|
||||
SurfaceId = surfaceId;
|
||||
Command = command;
|
||||
Payload = payload;
|
||||
Operation = operation ?? throw new ArgumentNullException(nameof(operation));
|
||||
Payload = payload ?? CultMeshOperationPayload.Empty;
|
||||
IssuedAt = issuedAt;
|
||||
ClientId = clientId;
|
||||
}
|
||||
|
|
@ -143,9 +281,11 @@ namespace GameCult.Eve.Surface
|
|||
|
||||
public string SurfaceId { get; }
|
||||
|
||||
public string Command { get; }
|
||||
public CultMeshOperationInvocationDescriptor Operation { get; }
|
||||
|
||||
public IReadOnlyDictionary<string, string> Payload { get; }
|
||||
public string Command => Operation.OperationId;
|
||||
|
||||
public CultMeshOperationPayload Payload { get; }
|
||||
|
||||
public DateTimeOffset IssuedAt { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
{
|
||||
"name": "GameCult.Eve.Surface",
|
||||
"rootNamespace": "GameCult.Eve.Surface",
|
||||
"references": [],
|
||||
"references": [
|
||||
"GameCult.Mesh"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"GameCult.Caching.dll",
|
||||
"GameCult.Mesh.dll",
|
||||
"MessagePack.Annotations.dll"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
|
|
|
|||
|
|
@ -42,6 +42,29 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
foreach (var child in component.Children)
|
||||
element.Add(LowerComponent(child, document, commandSink));
|
||||
|
||||
foreach (var slot in component.EmbeddedDocuments)
|
||||
{
|
||||
var nested = LowerEmbeddedDocument(slot, commandSink);
|
||||
if (nested != null)
|
||||
element.Add(nested);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
private VisualElement? LowerEmbeddedDocument(
|
||||
EveEmbeddedDocumentSlot slot,
|
||||
Action<EveSurfaceCommandRequest>? commandSink)
|
||||
{
|
||||
var document = _options.EmbeddedDocumentResolver?.Invoke(slot);
|
||||
if (document == null)
|
||||
return null;
|
||||
|
||||
var element = LowerComponent(document.Surface.Root, document, commandSink);
|
||||
element.name = SafeName(string.IsNullOrWhiteSpace(slot.SlotId) ? document.Surface.Id : slot.SlotId);
|
||||
element.AddToClassList("eve-embedded-document");
|
||||
element.AddToClassList($"eve-embedded-kind-{SafeClass(slot.PresentationKind)}");
|
||||
ApplyStyleTokens(element, document.Surface.Styles);
|
||||
return element;
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +152,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
{
|
||||
["value"] = evt.newValue ?? ""
|
||||
};
|
||||
EmitCommand(document, component, command, payload, commandSink);
|
||||
EmitCommand(document, component, command, GameCult.Mesh.CultMesh.OperationPayload(payload), commandSink);
|
||||
});
|
||||
return field;
|
||||
}
|
||||
|
|
@ -155,7 +178,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
document,
|
||||
component,
|
||||
command,
|
||||
new Dictionary<string, string>(component.Props, StringComparer.Ordinal),
|
||||
GameCult.Mesh.CultMesh.OperationPayload(component.Props),
|
||||
commandSink);
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +186,7 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
EveSurfaceDocument document,
|
||||
EveSurfaceComponent component,
|
||||
string command,
|
||||
IReadOnlyDictionary<string, string> payload,
|
||||
GameCult.Mesh.CultMeshOperationPayload payload,
|
||||
Action<EveSurfaceCommandRequest>? commandSink)
|
||||
{
|
||||
if (commandSink == null || string.IsNullOrWhiteSpace(command))
|
||||
|
|
@ -172,12 +195,25 @@ namespace GameCult.Eve.UnityUIToolkit
|
|||
commandSink(new EveSurfaceCommandRequest(
|
||||
document.ProviderId,
|
||||
document.Surface.Id,
|
||||
command,
|
||||
ResolveOperation(document, command),
|
||||
payload,
|
||||
DateTimeOffset.UtcNow,
|
||||
"unity-uitoolkit"));
|
||||
}
|
||||
|
||||
private static GameCult.Mesh.CultMeshOperationInvocationDescriptor ResolveOperation(
|
||||
EveSurfaceDocument document,
|
||||
string command)
|
||||
{
|
||||
foreach (var template in document.Commands)
|
||||
{
|
||||
if (string.Equals(template.Command, command, StringComparison.Ordinal))
|
||||
return GameCult.Mesh.CultMesh.OperationInvocation(template.Operation);
|
||||
}
|
||||
|
||||
return GameCult.Mesh.CultMesh.OperationInvocation(command);
|
||||
}
|
||||
|
||||
private static Label TitleLabel(string text)
|
||||
{
|
||||
var label = new Label(text);
|
||||
|
|
|
|||
Loading…
Reference in a new issue