Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ toward the Fluent theme per the migration hub's `architecture-patterns.md` §12.

## Why a global token system (and why it renders headlessly)

`FwAvaloniaApp` adds `new FluentTheme()`, which ships ~14px fonts and ~32px control floors app-wide.
`FwAvaloniaApp` adds Semi core plus Ursa Semi (no Fluent), which ships ~14px fonts and ~32px control floors app-wide.
Density/borders must be layered ON TOP of Fluent. The reliable mechanism — proven by a prior agent —
is **per-control-tree `.Styles`**, added to each view's own `Styles` collection by its view ctor.
App-level `Application.Styles` did **not** apply in the headless test app, so that path is rejected.
Expand Down Expand Up @@ -42,6 +42,11 @@ sit inside a `Border.fwFieldHost` that supplies the box.
directly (not three independently-maintained copies) -- see
`Src/Common/FwAvalonia/FwThemeResources.cs`.

**Font family:** Segoe UI, named by `FwUiFontFamily`. `FwUiFont.ApplyTo` registers
bundled Andika and points Semi's font resources at the theme family stack. Named
UI-language fallbacks and the platform default follow it. Data text keeps its
writing-system font. Measure UI text with `FwUiFont.UiFontFamily`. See ADR 0004.

**Control height:** `TextBox`/`ComboBox`/`Button` `MinHeight = 24` (WinForms runs ~21-23px; 24 is the
pointer-accessibility floor — see the "Why `DialogMinControlHeight` is 24, not 22" note below — still far
from Fluent's ~32px).
Expand Down
5 changes: 3 additions & 2 deletions Build/Src/FwBuildTasks/GenerateTokenKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ public class GenerateTokenKeys : Task
{
private static readonly XNamespace XamlNs = "http://schemas.microsoft.com/winfx/2006/xaml";

/// <summary>Every top-level ThemeDictionary entry becomes a key constant.</summary>
/// <summary>Every top-level ThemeDictionary entry becomes a key constant. FontFamily
/// entries are included, so the interface font family has a constant too.</summary>
private static readonly HashSet<string> TokenElementNames = new HashSet<string>(
new[] { "SolidColorBrush", "Color", "Double", "Thickness", "CornerRadius", "StaticResource" });
new[] { "SolidColorBrush", "Color", "Double", "Thickness", "CornerRadius", "FontFamily", "StaticResource" });

/// <summary>.axaml files whose EVERY x:Key becomes a generated constant.</summary>
[Required]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# The Avalonia interface font is controlled by the theme

LT-22854. The `FwUiFontFamily` token names Segoe UI as the default interface
font on Windows. Segoe UI preserves the density of the current interface; the
proposed Andika default has taller line metrics. Line-height tokens remain a
separate decision.

`FwUiFont.ApplyTo` registers the bundled Andika collection and assigns the
interface font stack to Semi's `DefaultFontFamily`,
`ContentControlThemeFontFamily`, and `SemiFontFamilyRegular` resources. The
stack combines the theme default, fallbacks for the UI language, and the
platform default. It is chosen at startup and applies across the application:
menus and other interface chrome should not change when the project changes.

The language project's `UiElement` style continues to control UI-writing-system
text drawn in Views; Avalonia interface chrome does not read that style. Data
text continues to use its writing-system font. Andika 7.000 remains bundled
in a private collection; a plain `Andika` family still names the system font
for writing-system data.

Windows validation must measure density, clipping and script glyph fallback.
Screenshot baselines still depict Andika and must be refreshed on Windows.
1 change: 1 addition & 0 deletions Docs/architecture/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ editing the next time the code changes, it belongs in one of the other three.
| [0001](0001-alias-semi-semantic-tokens.md) | Alias Semi's semantic tokens instead of inventing a FieldWorks palette |
| [0002](0002-whole-tree-token-hygiene-check.md) | token-hygiene.ps1 enforces full conformance across the Avalonia surface, with no grandfathering |
| [0003](0003-geometric-assertions-not-pixel-diff.md) | Assert geometry, not pixel diffs, for layout parity |
| [0004](0004-interface-font-controlled-by-the-theme.md) | Control the Avalonia interface font through a theme token; data text keeps its writing-system font |
5 changes: 3 additions & 2 deletions Docs/migration/adjust-the-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,9 @@ host. Treat difficulty here as a design signal, not a tooling problem.
- **The host window is WinForms, not Avalonia.** Title bar, icon, and close
button belong to the modal host form. You cannot style them from the
`.axaml`, and neither loop above shows them.
- **Converted dialogs render on the Fluent background, not WinForms grey**, and
use the Fluent font family. That is stack-wide, not something your dialog did.
- **Converted dialogs render on the Semi theme background, not WinForms grey**,
and use the FieldWorks interface font (`FwUiFontFamily`,
ADR 0004). That is stack-wide, not something your dialog did.
Changing it means editing `DialogTheme.axaml`'s `fwDialogRoot` style, which
restyles **every** converted dialog -- treat it as its own change, with dark
mode considered.
Expand Down
Binary file modified Docs/migration/baseline-screenshots/AddNewSense-02-populated.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/Chooser-01-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/CreateFeature-01-empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/EntryGo-04-row-selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/FeatureChooser-01-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/InsertEntry-02-populated.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/MsaCreator-01-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified Docs/migration/baseline-screenshots/Options-01-initial.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion Src/Common/FwAvalonia/Detail/FwFieldControls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public static double ComputeWsAbbrevColumnWidth(DetailModel model)
if (model == null)
return FwAvaloniaDensity.WsAbbrevWidth;

var typeface = new Typeface(FontFamily.Default);
var typeface = new Typeface(FwUiFont.UiFontFamily);
double widest = 0;
foreach (var abbrev in model.Fields.Where(f => f.IsMultiStringRow)
.SelectMany(f => f.Values).Select(v => v.WsAbbrev)
Expand Down
2 changes: 2 additions & 0 deletions Src/Common/FwAvalonia/FwAvaloniaApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public override void Initialize()
// Shared dialog spacing/border tokens, so the Dialog* keys this assembly
// publishes as generated constants are actually registered here too.
MergeTokens("avares://FwAvaloniaTheme/Tokens/DialogTokens.axaml");
// After the token merge: the interface font family is itself a token.
FwUiFont.ApplyTo(this);
}

private void MergeTokens(string avaresUri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private sealed class DisposableControl : Control, IDisposable
public void Dispose() => DisposeCount++;
}

[Test]
[AvaloniaTest]
public void DisposeDialogResources_DisposesDisposableBodyAndViewModel()
{
var body = new DisposableControl();
Expand All @@ -58,7 +58,7 @@ public void DisposeDialogResources_DisposesDisposableBodyAndViewModel()
Assert.That(vm.DisposeCount, Is.EqualTo(1), "the VM is disposed if IDisposable (it owns its resources)");
}

[Test]
[AvaloniaTest]
public void DisposeDialogResources_NonDisposableViewModel_DoesNotThrow()
{
Assert.DoesNotThrow(() => AvaloniaDialogHost.DisposeDialogResources(new Control(), new PlainVm()));
Expand Down
12 changes: 12 additions & 0 deletions Src/Common/FwAvalonia/FwAvaloniaTests/DetailPreviewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ public void Preview_WsText_UsesConfiguredFonts()
Assert.That(Find<TextBox>(window, "LexemeFormEditor.en").FontFamily.Name, Is.EqualTo("Times New Roman"));
}

[AvaloniaTest]
public void Preview_WsText_KeepsWsFonts_WhileLabelsUseTheInterfaceFont()
{
var window = ShowPreview();
Assert.That(Find<TextBox>(window, "LexemeFormEditor.seh").FontFamily.Name, Is.EqualTo("Charis SIL"));
Assert.That(Find<TextBox>(window, "LexemeFormEditor.en").FontFamily.Name, Is.EqualTo("Times New Roman"));

var label = window.GetVisualDescendants().OfType<TextBlock>()
.First(t => !string.IsNullOrEmpty(t.Text) && t.FindAncestorOfType<TextBox>() == null);
Assert.That(label.FontFamily, Is.EqualTo(SIL.FieldWorks.Common.FwAvalonia.FwUiFont.UiFontFamily));
}

[AvaloniaTest]
public void Preview_MorphTypeChooser_UsesSharedOptionPicker_AndReturnsFocus()
{
Expand Down
Loading
Loading