Skip to content

Commit 6406a4f

Browse files
Merge pull request #270 from ktsu-dev/claude/project-thread-4d94d3
Fix the editor's unit minus and warning colour, and tighten the gallery
2 parents 490c1d4 + c4ee053 commit 6406a4f

11 files changed

Lines changed: 136 additions & 11 deletions

File tree

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ Use `SchemaSerializer` for JSON serialization with `System.Text.Json`. The seria
235235
The **Schema.Editor** is an ImGui-based desktop application for visually creating and editing `.schema.json` files.
236236
The [editor gallery](docs/gallery/README.md) shows what it looks like, regenerated from `main` on every build.
237237

238+
[![The member grid with a member's unit, range, default and interpolation open](docs/gallery/member-metadata.png)](docs/gallery/README.md)
239+
238240
### Running the Editor
239241

240242
```shell

‎Schema.Editor/CodeGeneratorPanel.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ private void ShowGenerateButton(Schema schema, SchemaCodeGenerator codeGenerator
134134
{
135135
if (!schema.CanResolvePaths)
136136
{
137-
using (EditorTheme.Warning())
137+
using (EditorTheme.SeverityText(SchemaValidationSeverity.Warning))
138138
{
139139
ImGui.TextUnformatted("Save the schema before generating: output paths are relative to it.");
140140
}

‎Schema.Editor/MemberSemanticsPanel.cs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,13 @@ private void ShowUnitField(SchemaMember member)
114114
{
115115
ShowMetadataLabel("Unit:");
116116

117-
bool clicked = ImGui.Button(member.Unit is null ? SchemaEditor.NoneOption : member.Unit.ToString(), new Vector2(FieldWidth, 0));
117+
bool clicked = ImGui.Button(member.Unit is null ? SchemaEditor.NoneOption : UnitText.ForDisplay(member.Unit), new Vector2(FieldWidth, 0));
118118
ImGuiProbes.MarkItem("Unit");
119119

120120
if (member.Unit is not null && ImGui.IsItemHovered())
121121
{
122122
ImGui.SetTooltip(UnitRegistry.TryResolve(member.Unit, out IUnit? resolved, out string error)
123-
? $"{resolved!.Name} ({resolved.Symbol})"
123+
? UnitText.ForDisplay($"{resolved!.Name} ({resolved.Symbol})")
124124
: error);
125125
}
126126

‎Schema.Editor/Popups.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ internal void OpenUnitList(string title, string label, IEnumerable<IUnit> units,
9494
{
9595
List<IUnit> choices = [.. units];
9696
IUnit? selected = UnitRegistry.TryResolve(current, out IUnit? resolved, out _) ? resolved : null;
97-
PopupUnitList.Open(title, label, choices, selected, unit => $"{unit.Name} ({unit.Symbol})", onConfirm!);
97+
PopupUnitList.Open(title, label, choices, selected, unit => UnitText.ForDisplay($"{unit.Name} ({unit.Symbol})"), onConfirm!);
9898
});
9999

100100
internal void Update()

‎Schema.Editor/SchemaEditor.Diagnostics.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ private void ShowDiagnostic(SchemaValidationIssue issue)
110110
{
111111
bool isError = issue.Severity == SchemaValidationSeverity.Error;
112112

113-
using (EditorTheme.Severity(issue.Severity))
113+
using (EditorTheme.SeverityText(issue.Severity))
114114
{
115115
ImGui.TextUnformatted(isError ? "Error" : "Warning");
116116
}

‎Schema.Editor/SchemaEditor.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public partial class SchemaEditor
3737
#pragma warning disable IDE0052 // Remove unread private member - reference needed to prevent GC
3838
private readonly IntervalAction? autoSaveOptionsAction;
3939
#pragma warning restore IDE0052
40-
private ImGuiWidgets.DividerContainer DividerContainerCols { get; init; }
40+
internal ImGuiWidgets.DividerContainer DividerContainerCols { get; init; }
4141

4242
internal IUndoRedoService UndoRedo { get; }
4343

‎Schema.Editor/UnitText.cs‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2023-2026 ktsu-dev contributors
2+
3+
namespace ktsu.Schema.Editor;
4+
5+
using Hexa.NET.ImGui;
6+
7+
/// <summary>
8+
/// Spells a unit's text in characters the editor's font can draw.
9+
/// </summary>
10+
/// <remarks>
11+
/// <para>
12+
/// Unit symbols come from ktsu.Semantics.Quantities and write a negative exponent with the
13+
/// superscript minus, U+207B: <c>s⁻¹</c>, <c>m·s⁻²</c>. The font ImGuiApp ships, JetBrains Mono
14+
/// Nerd Font, has every superscript digit and the superscript plus but not that one character, so
15+
/// it drew as the fallback glyph and a frequency read <c>s?¹</c>. Widening the glyph ranges cannot
16+
/// help, because the glyph is missing from the font rather than from the ranges.
17+
/// </para>
18+
/// <para>
19+
/// Where the font has no glyph for it, the minus is drawn as a macron, U+00AF, which sits at the
20+
/// height of the superscript digits beside it and is the usual stand-in for a raised minus. This is
21+
/// display only: what is written to the schema file is untouched, and as soon as the font gains the
22+
/// real glyph it is drawn instead.
23+
/// </para>
24+
/// </remarks>
25+
internal static class UnitText
26+
{
27+
/// <summary>The superscript minus unit symbols use for negative exponents.</summary>
28+
internal const char SuperscriptMinus = '⁻';
29+
30+
/// <summary>The raised bar drawn in its place when the font has no superscript minus.</summary>
31+
internal const char RaisedBar = '¯';
32+
33+
/// <summary>
34+
/// Returns <paramref name="text"/> as it should be drawn in the current font.
35+
/// </summary>
36+
/// <remarks>Call during a frame: whether the font has the glyph is asked of the font in use.</remarks>
37+
/// <param name="text">A unit's symbol, or text containing one.</param>
38+
/// <returns>The text, with the superscript minus replaced when the font cannot draw it.</returns>
39+
internal static string ForDisplay(string text) =>
40+
text.Contains(SuperscriptMinus) && !FontHasSuperscriptMinus()
41+
? text.Replace(SuperscriptMinus, RaisedBar)
42+
: text;
43+
44+
private static bool FontHasSuperscriptMinus()
45+
{
46+
ImFontPtr font = ImGui.GetFont();
47+
return !font.IsNull && font.IsGlyphInFont(SuperscriptMinus);
48+
}
49+
}

‎tests/Schema.Editor.UITests/Gallery/EditorGallery.cs‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,18 @@ public sealed class EditorGallery
3131
/// <summary>The environment variable naming the directory the gallery is written to.</summary>
3232
internal const string OutputVariable = "SCHEMA_GALLERY_OUT";
3333

34-
/// <summary>The display the gallery is drawn at: larger than a test's, so the panels have room.</summary>
35-
internal static readonly HarnessOptions Display = new() { Width = 1440, Height = 900 };
34+
/// <summary>
35+
/// The display the gallery is drawn at: larger than a test's, so the panels have room, and no
36+
/// taller than the sample schema's tree, so a picture is of the editor rather than of the empty
37+
/// space under it.
38+
/// </summary>
39+
internal static readonly HarnessOptions Display = new() { Width = 1480, Height = 620 };
40+
41+
/// <summary>
42+
/// The share of the window the schema tree is given in the gallery: enough that the tree's
43+
/// tab labels, Code Generators included, are drawn whole rather than elided.
44+
/// </summary>
45+
private const float LeftPaneShare = 0.27f;
3646

3747
private static readonly Lazy<string> TemporaryOutput = new(() =>
3848
Path.Join(Path.GetTempPath(), $"schema-gallery-{Guid.NewGuid():N}"));
@@ -66,10 +76,11 @@ public void Photograph(string name)
6676
GalleryEntry entry = GalleryCatalog.Entries.Single(candidate => candidate.Name == name);
6777

6878
EditField.Reset();
69-
using EditorHarness harness = EditorHarness.Start(Display);
79+
using EditorHarness harness = EditorHarness.Start(entry.Display ?? Display);
7080
try
7181
{
7282
Assert.IsTrue(GalleryFonts.Load(), "ImGuiApp's own font could not be found, so the pictures would not look like the editor.");
83+
harness.Editor.DividerContainerCols.SetSizesFromList([LeftPaneShare, 1f - LeftPaneShare]);
7384
harness.App.Mouse.MoveTo(-100f, -100f);
7485
harness.App.Step(2);
7586

‎tests/Schema.Editor.UITests/Gallery/GalleryCatalog.cs‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@ internal static class GalleryCatalog
2828
"The editor opens on whatever was open last, or on nothing. The File menu creates, opens and saves `.schema.json` files and keeps the recent ones to hand.",
2929
harness =>
3030
{
31+
// Open Recent is disabled until something has been opened, so the picture starts
32+
// from an editor that has been used rather than one just installed.
33+
harness.Editor.Options.RecordRecentFile(GallerySchema.Path);
3134
harness.OpenMenu("File");
3235
Park(harness);
3336
})
3437
{
35-
Crop = _ => new Rectangle(0, 0, 720, 450),
38+
Crop = _ => new Rectangle(0, 0, 560, 200),
3639
},
3740
new(
3841
"Editing a class",
@@ -126,7 +129,11 @@ internal static class GalleryCatalog
126129
harness.StepUntil(() => harness.IsOnScreen("theme-card/Dracula"), "the theme browser opening");
127130
harness.App.Step(3);
128131
Park(harness);
129-
}),
132+
})
133+
{
134+
// The browser is a fixed size, taller than the editor's own pictures.
135+
Display = new() { Width = 1480, Height = 800 },
136+
},
130137
];
131138

132139
/// <summary>Opens the sample schema with its main class selected, as a user would find it.</summary>

‎tests/Schema.Editor.UITests/Gallery/GalleryEntry.cs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ internal sealed record GalleryEntry(string Name, string Description, Action<Edit
2424
/// </summary>
2525
public Func<EditorHarness, Rectangle?>? Crop { get; init; }
2626

27+
/// <summary>
28+
/// Gets the display this picture is drawn at, or null for <see cref="EditorGallery.Display"/>.
29+
/// </summary>
30+
public HarnessOptions? Display { get; init; }
31+
2732
/// <summary>Gets the file name the picture is written under, without its extension.</summary>
2833
public string Slug => MakeSlug(Name);
2934

0 commit comments

Comments
 (0)