Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Web Inspector: Add editing capabilities for font variation axes in Fo…
…nts sidebar panel

https://bugs.webkit.org/show_bug.cgi?id=249535

Reviewed by Patrick Angle.

Variable fonts expose variation axes to control specific aspects of the font,
like thickness, stretch or slant. The Fonts sidebar panel already lists the
available variation axes for the primary font used on the selected node.

This patch augments the Fonts sidebar panel with editing controls to interactively
adjust the values for variation axes and get immediate visual feedback of the results.

There are two types of variation axes: registered and custom.

Registered axes values map to a corresponding CSS font property:
`wght` -> font-weight
`wdth` -> font-stretch
`ital` -> font-style
`slnt` -> font-style

Custom axes and their values are always written to `font-variation-settings`.

If a registered axis is declared in the value of `font-variation-settings`,
its value must always be written there instead of the corresponding font property
else it will be overriden by the value of `font-variation-settings`.

In this implementation, all edits made using the controls in the Fonts sidebar panel
are written to the selected node's inline style to ensure immediate visual feedback
at the expense of scoping the font style changes very narrowly to the selected node only.

A future iteration will improve this by identifying the appropriate CSS rules
in the cascade to write to.

* LayoutTests/inspector/model/font-calculate-properties-expected.txt:
* LayoutTests/inspector/model/font-calculate-properties.html:
* LayoutTests/inspector/model/font-styles-write-variation-expected.txt: Added.
* LayoutTests/inspector/model/font-styles-write-variation.html: Added.

Updated existing test to use the new `WI.FontStyles` model.
Added new test to validate variation axis writing logic.

* Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js:
* Source/WebInspectorUI/UserInterface/Main.html:
* Source/WebInspectorUI/UserInterface/Models/Font.js:
(WI.Font.prototype.variationAxis):
(WI.Font):
(WI.Font.prototype.calculateFontProperties): Deleted.
(WI.Font.prototype._calculateProperties): Deleted.
(WI.Font.prototype._calculateFontFeatureAxes): Deleted.
(WI.Font.prototype._calculateFontVariationAxes): Deleted.
(WI.Font.prototype._parseFontFeatureOrVariationSettings): Deleted.

Moved parsing-related logic from `WI.Font` to a new `WI.FontStyles` model.
This object maintains state of edits made to variation axes over time.
It handles where to write variation axis values.

* Source/WebInspectorUI/UserInterface/Models/FontStyles.js: Copied from Source/WebInspectorUI/UserInterface/Models/Font.js.
(WI.FontStyles):
(WI.FontStyles.prototype.get featuresMap):
(WI.FontStyles.prototype.get variationsMap):
(WI.FontStyles.prototype.get propertiesMap):
(WI.FontStyles.prototype.get significantChangeSinceLastRefresh):
(WI.FontStyles.fontPropertyForAxisTag):
(WI.FontStyles.axisValueToFontPropertyValue):
(WI.FontStyles.fontPropertyValueToAxisValue):
(WI.FontStyles.prototype.writeFontVariation):
(WI.FontStyles.prototype.refresh):
(WI.FontStyles.prototype._calculateFontProperties):
(WI.FontStyles.prototype._calculateProperties):
(WI.FontStyles.prototype._calculateFontFeatureAxes):
(WI.FontStyles.prototype._calculateFontVariationAxes):
(WI.FontStyles.prototype._parseFontFeatureOrVariationSettings):

Most contents of `WI.FontStyles` are copied over from `WI.Font` verbatim.

* Source/WebInspectorUI/UserInterface/Test.html:
* Source/WebInspectorUI/UserInterface/Views/DetailsSection.css:
(.details-section > .content > .group:has(.row.font-variation)):
(.details-section > .content > .group > .row:is(.simple, .font-variation):has(.warning)):
(.details-section > .content > .group > .row:is(.simple, .font-variation) > .warning):
(.details-section > .content > .group > .row.simple:has(.warning)): Deleted.
(.details-section > .content > .group > .row.simple > .warning): Deleted.
* Source/WebInspectorUI/UserInterface/Views/FontDetailsPanel.js:
(WI.FontDetailsPanel):
(WI.FontDetailsPanel.prototype.refresh):
(WI.FontDetailsPanel.prototype.detached):
(WI.FontDetailsPanel.prototype.layout):
(WI.FontDetailsPanel.prototype.initialLayout):
(WI.FontDetailsPanel.prototype._createDetailsSectionRowForProperty):
(WI.FontDetailsPanel.prototype._formatPropertyValue):
(WI.FontDetailsPanel.prototype._handleFontVariationValueChanged):
(WI.FontDetailsPanel.prototype._formatSizeValue): Deleted.
(WI.FontDetailsPanel.prototype._formatStyleValue): Deleted.
(WI.FontDetailsPanel.prototype._formatSimpleSingleValue): Deleted.
(WI.FontDetailsPanel.prototype._formatVariationValue): Deleted.
(WI.FontDetailsPanel.prototype._createVariationValueElement): Deleted.
(WI.FontDetailsPanel.prototype._hasVariationValue): Deleted.

Inside `FontDetailsPanel.layout()` the structure for editing controls is created.
Inside `FontDetailsPanel.refresh()` the values for editing controls are updated.
This ensures the UI remains in sync when changes occur in the Styles sidebar panel
or new styles apply as a result of DOM mutations, matching media queries, etc.

* Source/WebInspectorUI/UserInterface/Views/FontVariationDetailsSectionRow.css: Added.
(.details-section > .content > .group > .row.font-variation):
(.details-section > .content > .group > .row.font-variation > .label:not(:empty)):
(.details-section > .content > .group > .row.font-variation > .tag):
(.details-section > .content > .group > .row.font-variation > .value):
(.details-section > .content > .group > .row.font-variation > .variation-range):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]::-webkit-slider-thumb):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]::-webkit-slider-thumb:active):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]::-webkit-slider-runnable-track):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]::before,):
(.details-section > .content > .group > .row.font-variation > .variation-range > input[type="range"]::after):
(.details-section > .content > .group > .row.font-variation > .variation-range > .variation-minvalue):
(.details-section > .content > .group > .row.font-variation > .variation-range > .variation-maxvalue):
* Source/WebInspectorUI/UserInterface/Views/FontVariationDetailsSectionRow.js: Added.
(WI.FontVariationDetailsSectionRow):
(WI.FontVariationDetailsSectionRow.prototype.get tag):
(WI.FontVariationDetailsSectionRow.prototype.set tag):
(WI.FontVariationDetailsSectionRow.prototype.get label):
(WI.FontVariationDetailsSectionRow.prototype.set label):
(WI.FontVariationDetailsSectionRow.prototype.get value):
(WI.FontVariationDetailsSectionRow.prototype.set value):
(WI.FontVariationDetailsSectionRow.prototype.get warningMessage):
(WI.FontVariationDetailsSectionRow.prototype.set warningMessage):
(WI.FontVariationDetailsSectionRow.prototype._formatAxisValueAsString):
(WI.FontVariationDetailsSectionRow.prototype._getAxisResolution):

`WI.FontVariationDetailsSectionRow` encapsulates the layout for editing controls:
the interactive slider, the track with min/max values, warnings for out-of-range values.

* Source/WebInspectorUI/UserInterface/Views/Variables.css:
(:root):
(@media (prefers-color-scheme: dark) :root):

Canonical link: https://commits.webkit.org/258503@main
  • Loading branch information
rcaliman-apple committed Jan 5, 2023
1 parent b30c948 commit b948d01
Show file tree
Hide file tree
Showing 17 changed files with 1,054 additions and 293 deletions.
22 changes: 11 additions & 11 deletions LayoutTests/inspector/model/font-calculate-properties-expected.txt
@@ -1,4 +1,4 @@
Test for WI.Font.
Test for WI.FontStyles.

Basic Properties Test
Feature Property Ligatures Test
Expand All @@ -10,47 +10,47 @@ Feature Property East Asian Test
Unregisted Feature Property Test
Variable Font Test

== Running test suite: WI.Font
-- Running test case: WI.Font.BasicPropertiesTest
== Running test suite: WI.FontStyles
-- Running test case: WI.FontStyles.BasicPropertiesTest
PASS: Font size property value should be '2rem'.
PASS: Font size property value should be 'italic'.
PASS: Font size property value should be '700'.
PASS: Font size property value should be '150%'.

-- Running test case: WI.Font.FeaturePropertyLigaturesTest
-- Running test case: WI.FontStyles.FeaturePropertyLigaturesTest
PASS: Property value should be 'no-common-ligatures'.
PASS: 'calt' feature value should be '1'.
PASS: 'dlig' feature value should be '1'.
PASS: 'hlig' feature value should be '1'.

-- Running test case: WI.Font.FeaturePropertyPositionTest
-- Running test case: WI.FontStyles.FeaturePropertyPositionTest
PASS: Property value should be 'super'.
PASS: 'sups' feature value should be '0'.
PASS: 'subs' feature value should be '1'.

-- Running test case: WI.Font.FeaturePropertyCapitalsTest
-- Running test case: WI.FontStyles.FeaturePropertyCapitalsTest
PASS: Property value should be 'all-small-caps'.
PASS: 'c2sc' feature value should be '1'.
PASS: 'smcp' feature value should be '1'.

-- Running test case: WI.Font.FeaturePropertyNumericTest
-- Running test case: WI.FontStyles.FeaturePropertyNumericTest
PASS: Property value should be 'lining-nums tabular-nums stacked-fractions'.
PASS: 'zero' feature value should be '1'.

-- Running test case: WI.Font.FeaturePropertyAlternatesTest
-- Running test case: WI.FontStyles.FeaturePropertyAlternatesTest
PASS: Property value should be 'historical-forms'.
PASS: 'hist' feature value should be '1'.

-- Running test case: WI.Font.FeaturePropertyEastAsianTest
-- Running test case: WI.FontStyles.FeaturePropertyEastAsianTest
PASS: Property value should be 'jis78 full-width'.
PASS: 'jp78' feature value should be '1'.

-- Running test case: WI.Font.UnregisteredFeaturePropertyTest
-- Running test case: WI.FontStyles.UnregisteredFeaturePropertyTest
PASS: 'appl' feature value should be '1'.

-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.Font.VariableFontTest
-- Running test case: WI.FontStyles.VariableFontTest
PASS: Width property value should be '50%'.
PASS: Width axis name should be 'Width'.
PASS: Width axis value should be '777'.
Expand Down
24 changes: 12 additions & 12 deletions LayoutTests/inspector/model/font-calculate-properties.html
Expand Up @@ -19,7 +19,7 @@
{
let documentNode;

let suite = InspectorTest.createAsyncSuite("WI.Font");
let suite = InspectorTest.createAsyncSuite("WI.FontStyles");

function addTestCase({name, description, setup, selector, propertyMapHandlers})
{
Expand All @@ -37,7 +37,7 @@

await cssStyles.refreshIfNeeded();

let {propertiesMap, featuresMap, variationsMap} = cssStyles.computedPrimaryFont.calculateFontProperties(cssStyles);
let {propertiesMap, featuresMap, variationsMap} = new WI.FontStyles(cssStyles);

await propertyMapHandlers(propertiesMap, featuresMap, variationsMap);
},
Expand All @@ -53,7 +53,7 @@
}

addTestCase({
name: "WI.Font.BasicPropertiesTest",
name: "WI.FontStyles.BasicPropertiesTest",
description: "Get the basic font properties for the node '#basic-properties-test'.",
selector: "#basic-properties-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -65,7 +65,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyLigaturesTest",
name: "WI.FontStyles.FeaturePropertyLigaturesTest",
description: "Get the font ligature property for the node '#feature-property-ligatures-test'.",
selector: "#feature-property-ligatures-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -78,7 +78,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyPositionTest",
name: "WI.FontStyles.FeaturePropertyPositionTest",
description: "Get the font position property for the node '#feature-property-position-test'.",
selector: "#feature-property-position-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -90,7 +90,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyCapitalsTest",
name: "WI.FontStyles.FeaturePropertyCapitalsTest",
description: "Get the font capitals property for the node '#feature-property-capitals-test'.",
selector: "#feature-property-capitals-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -102,7 +102,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyNumericTest",
name: "WI.FontStyles.FeaturePropertyNumericTest",
description: "Get the font numeric property for the node '#feature-property-numeric-test'.",
selector: "#feature-property-numeric-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -113,7 +113,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyAlternatesTest",
name: "WI.FontStyles.FeaturePropertyAlternatesTest",
description: "Get the font alternates property for the node '#feature-property-alternates-test'.",
selector: "#feature-property-alternates-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -124,7 +124,7 @@
});

addTestCase({
name: "WI.Font.FeaturePropertyEastAsianTest",
name: "WI.FontStyles.FeaturePropertyEastAsianTest",
description: "Get the font east asian property for the node '#feature-property-east-asian-test'.",
selector: "#feature-property-east-asian-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -135,7 +135,7 @@
});

addTestCase({
name: "WI.Font.UnregisteredFeaturePropertyTest",
name: "WI.FontStyles.UnregisteredFeaturePropertyTest",
description: "Get an unregistered feature property for the node '#unregistered-feature-property-test'.",
selector: "#unregistered-feature-property-test",
propertyMapHandlers(propertiesMap, featuresMap, variationsMap) {
Expand All @@ -144,7 +144,7 @@
});

addTestCase({
name: "WI.Font.VariableFontTest",
name: "WI.FontStyles.VariableFontTest",
description: "Get the font variations axes for the node '#variable-font-test'.",
async setup() {
return loadFontFace("normal normal 12px VariableFont", " ", "TestPage-VariableFont1Loaded");
Expand Down Expand Up @@ -245,7 +245,7 @@
</style>
</head>
<body onload="runTest();">
<p>Test for WI.Font.</p>
<p>Test for WI.FontStyles.</p>
<div>
<div id="basic-properties-test">Basic Properties Test</div>
<div id="feature-property-ligatures-test">Feature Property Ligatures Test</div>
Expand Down
@@ -0,0 +1,39 @@
Tests for WI.FontStyles.WriteFontVariation


== Running test suite: WI.FontStyles.WriteFontVariation
-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.FontStyles.WriteFontVariation.RegisteredAxisToFontProperty.Create
INFO: Writing variation axis "wdth" with value 100
PASS: Inline style has CSS property font-stretch
PASS: Found expected CSS declaration font-stretch: 100%

-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.FontStyles.WriteFontVariation.RegisteredAxisToFontProperty.Update
INFO: Writing variation axis "wdth" with value 100
PASS: Inline style has CSS property font-stretch
PASS: Found expected CSS declaration font-stretch: 100%

-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Update
INFO: Writing variation axis "wdth" with value 100
PASS: Inline style has CSS property font-variation-settings
PASS: Found expected CSS declaration font-variation-settings: "wdth" 100

-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Create
INFO: Writing variation axis "desc" with value 777
PASS: Inline style has CSS property font-variation-settings
PASS: Found expected CSS declaration font-variation-settings: "desc" 777

-- Running test setup.
PASS: Font should be loaded.
-- Running test case: WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Update
INFO: Writing variation axis "desc" with value 777
PASS: Inline style has CSS property font-variation-settings
PASS: Found expected CSS declaration font-variation-settings: "desc" 777

177 changes: 177 additions & 0 deletions LayoutTests/inspector/model/font-styles-write-variation.html
@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html>
<head>
<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
<script>

async function loadFontFace(fontDeclaration, text, eventName)
{
try {
await document.fonts.load(fontDeclaration, text);
TestPage.log("PASS: Font should be loaded.");
} catch {
TestPage.log("FAIL: Font should be loaded.");
}
TestPage.dispatchEventToFrontend(eventName);
}

function test()
{
let suite = InspectorTest.createAsyncSuite("WI.FontStyles.WriteFontVariation");

function loadFontFace(fontDeclaration, text, eventName)
{
return Promise.all([
InspectorTest.awaitEvent(eventName),
InspectorTest.evaluateInPage(`loadFontFace("${fontDeclaration}", "${text}", "${eventName}")`),
]);
}

function addTestCase({name, description, selector, variations, expectedInlineStyleProperties})
{
suite.addTestCase({
name,
description,
async setup() {
await loadFontFace("normal normal 12px VariableFont", " ", "TestPage-VariableFont1Loaded");
},
async test() {
let documentNode = await WI.domManager.requestDocument();
let nodeId = await documentNode.querySelector(selector);
let domNode = await WI.domManager.nodeForId(nodeId);
InspectorTest.assert(domNode, `Should find DOM Node for selector '${selector}'.`);

let cssStyles = WI.cssManager.stylesForNode(domNode);
InspectorTest.assert(cssStyles, `Should find CSS Styles for DOM Node.`);

await cssStyles.refreshIfNeeded();
let fontStyles = new WI.FontStyles(cssStyles);

for (let [tag, value] of Object.entries(variations)) {
InspectorTest.log(`INFO: Writing variation axis "${tag}" with value ${value}`);
fontStyles.writeFontVariation(tag, value);
}

await cssStyles.refreshIfNeeded();
fontStyles.refresh();

let properties = cssStyles.inlineStyle.visibleProperties;

for (let [name, value] of Object.entries(expectedInlineStyleProperties)) {
let cssProperty = properties.find(cssProperty => cssProperty.name === name) || null;

InspectorTest.expectNotNull(cssProperty, `Inline style has CSS property ${name}`);
InspectorTest.expectEqual(cssProperty.value, value, `Found expected CSS declaration ${name}: ${value}`);
}
},
});
}


addTestCase({
name: "WI.FontStyles.WriteFontVariation.RegisteredAxisToFontProperty.Create",
description: "A registered axis value is written to its corresponding font property; create if missing.",
selector: "#registered-axis-to-property-create",
variations: {
"wdth": 100,
},
expectedInlineStyleProperties: {
"font-stretch": "100%",
}
});

addTestCase({
name: "WI.FontStyles.WriteFontVariation.RegisteredAxisToFontProperty.Update",
description: "A registered axis value is written to its corresponding font property; update if found.",
selector: "#registered-axis-to-property-update",
variations: {
"wdth": 100,
},
expectedInlineStyleProperties: {
"font-stretch": "100%",
}
});

addTestCase({
name: "WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Update",
description: "A registered axis value is written to font-variation-settings if already defined there.",
selector: "#registered-axis-to-variation-settings-update",
variations: {
"wdth": 100,
},
expectedInlineStyleProperties: {
"font-variation-settings": "\"wdth\" 100",
}
});

addTestCase({
name: "WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Create",
description: "A custom axis value is written to font-variation-settings; create if missing.",
selector: "#custom-axis-to-variation-settings-create",
variations: {
"desc": 777,
},
expectedInlineStyleProperties: {
"font-variation-settings": "\"desc\" 777",
}
});

addTestCase({
name: "WI.FontStyles.WriteFontVariation.RegisteredAxisToVariationSettings.Update",
description: "A custom axis value is written to font-variation-settings; update if found.",
selector: "#custom-axis-to-variation-settings-update",
variations: {
"desc": 777,
},
expectedInlineStyleProperties: {
"font-variation-settings": "\"desc\" 777",
}
});

suite.runTestCasesAndFinish();
}
</script>
<style>

@font-face {
font-family: "VariableFont";
src: url("../../animations/font-variations/resources/Boxis-VF.ttf");
}

div {
font-family: VariableFont;
}

#registered-axis-to-property-create {

}

#registered-axis-to-property-update {
font-stretch: 50%;
}

#registered-axis-to-variation-settings-update {
font-variation-settings: "wdth" 50;
}

#custom-axis-to-variation-settings-create {

}

#custom-axis-to-existing-variation-settings-update {
font-variation-settings: "desc" 50;
}

</style>
</head>
<body onload="runTest();">
<p>Tests for WI.FontStyles.WriteFontVariation</p>

<div id="registered-axis-to-property-create"></div>
<div id="registered-axis-to-property-update"></div>
<div id="registered-axis-to-variation-settings-update"></div>
<div id="custom-axis-to-variation-settings-create"></div>
<div id="custom-axis-to-variation-settings-update"></div>

</body>
</html>

0 comments on commit b948d01

Please sign in to comment.