mirror of
https://github.com/zebrajr/ladybird.git
synced 2025-12-06 00:19:53 +01:00
LibWeb/CSS: Generate code for CSS dimension units
This commit is contained in:
parent
5e23df7d8a
commit
cbc019350b
|
|
@ -319,7 +319,7 @@ This is a single JSON object, describing each [CSS environment variable](https:/
|
|||
with the keys being the environment variable names, and the values being objects describing the variable's properties.
|
||||
This generates `EnvironmentVariable.h` and `EnvironmentVariable.cpp`.
|
||||
|
||||
Each entry has 3 properties, all taken from the
|
||||
Each entry has 3 properties, all taken from the spec:
|
||||
|
||||
| Field | Description |
|
||||
|--------------|---------------------------------------------------------------------|
|
||||
|
|
@ -333,3 +333,27 @@ The generated code provides:
|
|||
- `StringView to_string(EnvironmentVariable)` to convert the `EnvironmentVariable` back to a string
|
||||
- `ValueType environment_variable_type(EnvironmentVariable)` to get the variable's value type
|
||||
- `u32 environment_variable_dimension_count(EnvironmentVariable)` to get its dimension count
|
||||
|
||||
## Units.json
|
||||
|
||||
This is a JSON object with the keys being dimension type names, and the values being objects. Those objects' keys are
|
||||
unit names, and their values are data about each unit.
|
||||
It generates `Units.h` and `Units.cpp`.
|
||||
|
||||
Each unit has the following properties:
|
||||
|
||||
| Field | Description |
|
||||
|----------------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `is-canonical-unit` | Boolean, default `false`. Each dimension has one canonical unit. |
|
||||
| `number-of-canonical-unit` | Number. How many of the canonical units 1 of this is equivalent to. Ignore this for relative units, and the canonical unit itself. |
|
||||
| `is-relative-to` | String. Some length units are relative to the font or viewport. Set this to `"font"` or `"viewport"` for those. |
|
||||
|
||||
The generated code provides:
|
||||
- A `DimensionType` enum, listing each type of dimension that has units defined.
|
||||
- `Optional<DimensionType> dimension_for_unit(StringView)` for querying which dimension a unit applies to, if any.
|
||||
- A `FooUnit` enum for each dimension "foo", which lists all the units of that dimension.
|
||||
- For each of those...
|
||||
- `Optional<FooUnit> string_to_foo_unit(StringView)` for parsing a unit from a string.
|
||||
- `StringView to_string(FooUnit)` for serializing those units.
|
||||
- `double ratio_between_units(FooUnit, FooUnit)` to get a multiplier for converting the first unit into the second.
|
||||
- `bool is_absolute(LengthUnit)`, `bool is_font_relative(LengthUnit)`, `bool is_viewport_relative(LengthUnit)`, and `bool is_relative(LengthUnit)` for checking the category of length units.
|
||||
|
|
|
|||
|
|
@ -1082,6 +1082,7 @@ set(GENERATED_SOURCES
|
|||
CSS/PseudoElement.cpp
|
||||
CSS/QuirksModeStyleSheetSource.cpp
|
||||
CSS/TransformFunctions.cpp
|
||||
CSS/Units.cpp
|
||||
MathML/MathMLStyleSheetSource.cpp
|
||||
SVG/SVGStyleSheetSource.cpp
|
||||
Worker/WebWorkerClientEndpoint.h
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <AK/String.h>
|
||||
#include <LibWeb/CSS/SerializationMode.h>
|
||||
#include <LibWeb/CSS/Units.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
|
|
|||
182
Libraries/LibWeb/CSS/Units.json
Normal file
182
Libraries/LibWeb/CSS/Units.json
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
{
|
||||
"angle": {
|
||||
"deg": {
|
||||
"is-canonical-unit": true
|
||||
},
|
||||
"grad": {
|
||||
"number-of-canonical-unit": 0.9
|
||||
},
|
||||
"rad": {
|
||||
"number-of-canonical-unit": 57.2957795130823208767981548141051703324054724665643215491602438612028471483
|
||||
},
|
||||
"turn": {
|
||||
"number-of-canonical-unit": 360
|
||||
}
|
||||
},
|
||||
"flex": {
|
||||
"fr": {
|
||||
"is-canonical-unit": true
|
||||
}
|
||||
},
|
||||
"frequency": {
|
||||
"Hz": {
|
||||
"is-canonical-unit": true
|
||||
},
|
||||
"kHz": {
|
||||
"number-of-canonical-unit": 1000
|
||||
}
|
||||
},
|
||||
"length": {
|
||||
"cap": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"ch": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"cm": {
|
||||
"number-of-canonical-unit": 37.7952755905511811023622047244094488188976377952755905511811023622047244094
|
||||
},
|
||||
"dvb": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"dvh": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"dvi": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"dvmax": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"dvmin": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"dvw": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"em": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"ex": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"ic": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"in": {
|
||||
"number-of-canonical-unit": 96
|
||||
},
|
||||
"lh": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"lvb": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"lvh": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"lvi": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"lvmax": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"lvmin": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"lvw": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"mm": {
|
||||
"number-of-canonical-unit": 3.77952755905511811023622047244094488188976377952755905511811023622047244094
|
||||
},
|
||||
"pc": {
|
||||
"number-of-canonical-unit": 16
|
||||
},
|
||||
"pt": {
|
||||
"number-of-canonical-unit": 1.33333333333333333333333333333333333333333333333333333333333333333333333333
|
||||
},
|
||||
"px": {
|
||||
"is-canonical-unit": true
|
||||
},
|
||||
"Q": {
|
||||
"number-of-canonical-unit": 0.94488188976377952755905511811023622047244094488188976377952755905511811023
|
||||
},
|
||||
"rcap": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"rch": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"rem": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"rex": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"ric": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"rlh": {
|
||||
"relative-to": "font"
|
||||
},
|
||||
"svb": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"svh": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"svi": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"svmax": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"svmin": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"svw": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vb": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vh": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vi": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vmax": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vmin": {
|
||||
"relative-to": "viewport"
|
||||
},
|
||||
"vw": {
|
||||
"relative-to": "viewport"
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"dpcm": {
|
||||
"number-of-canonical-unit": 0.02645833333333333333333333333333333333333333333333333333333333333333333333
|
||||
},
|
||||
"dpi": {
|
||||
"number-of-canonical-unit": 0.01041666666666666666666666666666666666666666666666666666666666666666666666
|
||||
},
|
||||
"dppx": {
|
||||
"is-canonical-unit": true
|
||||
},
|
||||
"x": {
|
||||
"number-of-canonical-unit": 1
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
"ms": {
|
||||
"number-of-canonical-unit": 0.001
|
||||
},
|
||||
"s": {
|
||||
"is-canonical-unit": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -86,6 +86,15 @@ function (generate_css_implementation)
|
|||
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/TransformFunctions.json"
|
||||
)
|
||||
|
||||
invoke_cpp_generator(
|
||||
"Units.cpp"
|
||||
Lagom::GenerateCSSUnits
|
||||
"${LIBWEB_INPUT_FOLDER}/CSS/Units.json"
|
||||
"CSS/Units.h"
|
||||
"CSS/Units.cpp"
|
||||
arguments -j "${LIBWEB_INPUT_FOLDER}/CSS/Units.json"
|
||||
)
|
||||
|
||||
invoke_cpp_generator(
|
||||
"Keyword.cpp"
|
||||
Lagom::GenerateCSSKeyword
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ lagom_tool(GenerateCSSPseudoClass SOURCES GenerateCSSPseudoClass.cpp L
|
|||
lagom_tool(GenerateCSSPseudoElement SOURCES GenerateCSSPseudoElement.cpp LIBS LibMain)
|
||||
lagom_tool(GenerateCSSStyleProperties SOURCES GenerateCSSStyleProperties.cpp LIBS LibMain)
|
||||
lagom_tool(GenerateCSSTransformFunctions SOURCES GenerateCSSTransformFunctions.cpp LIBS LibMain)
|
||||
lagom_tool(GenerateCSSUnits SOURCES GenerateCSSUnits.cpp LIBS LibMain)
|
||||
lagom_tool(GenerateWindowOrWorkerInterfaces SOURCES GenerateWindowOrWorkerInterfaces.cpp LIBS LibMain LibIDL)
|
||||
lagom_tool(GenerateAriaRoles SOURCES GenerateAriaRoles.cpp LIBS LibMain)
|
||||
lagom_tool(GenerateNamedCharacterReferences SOURCES GenerateNamedCharacterReferences.cpp LIBS LibMain)
|
||||
|
|
|
|||
371
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp
Normal file
371
Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateCSSUnits.cpp
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "GeneratorUtil.h"
|
||||
#include <AK/GenericShorthands.h>
|
||||
#include <AK/SourceGenerator.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibMain/Main.h>
|
||||
|
||||
ErrorOr<void> generate_header_file(JsonObject& dimensions_data, Core::File& file);
|
||||
ErrorOr<void> generate_implementation_file(JsonObject& dimensions_data, Core::File& file);
|
||||
bool json_is_valid(JsonObject& dimensions_data, StringView json_path);
|
||||
|
||||
ErrorOr<int> ladybird_main(Main::Arguments arguments)
|
||||
{
|
||||
StringView generated_header_path;
|
||||
StringView generated_implementation_path;
|
||||
StringView json_path;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(generated_header_path, "Path to the Units header file to generate", "generated-header-path", 'h', "generated-header-path");
|
||||
args_parser.add_option(generated_implementation_path, "Path to the Units implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
|
||||
args_parser.add_option(json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path");
|
||||
args_parser.parse(arguments);
|
||||
|
||||
auto json = TRY(read_entire_file_as_json(json_path));
|
||||
VERIFY(json.is_object());
|
||||
auto dimensions_data = json.as_object();
|
||||
|
||||
if (!json_is_valid(dimensions_data, json_path))
|
||||
return 1;
|
||||
|
||||
auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
|
||||
auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
|
||||
|
||||
TRY(generate_header_file(dimensions_data, *generated_header_file));
|
||||
TRY(generate_implementation_file(dimensions_data, *generated_implementation_file));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool json_is_valid(JsonObject& dimensions_data, StringView json_path)
|
||||
{
|
||||
bool is_valid = true;
|
||||
String most_recent_dimension_name;
|
||||
dimensions_data.for_each_member([&](auto& dimension_name, JsonValue const& value) {
|
||||
// Dimensions should be in alphabetical order
|
||||
if (dimension_name.to_ascii_lowercase() < most_recent_dimension_name.to_ascii_lowercase()) {
|
||||
warnln("{}: Dimension `{}` is in the wrong position. Please keep this list alphabetical!", json_path, dimension_name);
|
||||
is_valid = false;
|
||||
}
|
||||
most_recent_dimension_name = dimension_name;
|
||||
|
||||
String most_recent_unit_name;
|
||||
Optional<String> canonical_unit;
|
||||
value.as_object().for_each_member([&](auto& unit_name, JsonValue const& unit_value) {
|
||||
auto& unit = unit_value.as_object();
|
||||
|
||||
// Units should be in alphabetical order
|
||||
if (unit_name.to_ascii_lowercase() < most_recent_unit_name.to_ascii_lowercase()) {
|
||||
warnln("{}: {} unit `{}` is in the wrong position. Please keep this list alphabetical!", json_path, dimension_name, unit_name);
|
||||
is_valid = false;
|
||||
}
|
||||
most_recent_unit_name = unit_name;
|
||||
|
||||
// A unit must have exactly 1 of:
|
||||
// - is-canonical-unit: true
|
||||
// - number-of-canonical-unit
|
||||
// - relative-to
|
||||
bool is_canonical_unit = unit.get_bool("is-canonical-unit"sv) == true;
|
||||
auto number_of_canonical_unit = unit.get_double_with_precision_loss("number-of-canonical-unit"sv);
|
||||
auto relative_to = unit.get_string("relative-to"sv);
|
||||
auto provided_count = (is_canonical_unit ? 1 : 0) + (number_of_canonical_unit.has_value() ? 1 : 0) + (relative_to.has_value() ? 1 : 0);
|
||||
if (provided_count != 1) {
|
||||
warnln("{}: {} unit `{}` must have exactly 1 of `is-canonical-unit: true`, `number-of-canonical-unit`, or `relative-to` provided.", json_path, dimension_name, unit_name);
|
||||
is_valid = false;
|
||||
}
|
||||
// Exactly 1 canonical unit is allowed.
|
||||
if (is_canonical_unit) {
|
||||
if (canonical_unit.has_value()) {
|
||||
warnln("{}: {} unit `{}` marked canonical, but `{}` was already. Must have exactly 1.", json_path, dimension_name, unit_name, canonical_unit.value());
|
||||
is_valid = false;
|
||||
} else {
|
||||
canonical_unit = unit_name;
|
||||
}
|
||||
}
|
||||
// Also, relative-to has fixed values and is only permitted for length units, at least for now.
|
||||
if (relative_to.has_value()) {
|
||||
if (dimension_name == "length"sv) {
|
||||
if (!first_is_one_of(relative_to.value(), "font"sv, "viewport"sv)) {
|
||||
warnln("{}: {} unit `{}` is marked as relative to `{}`, which is unsupported.", json_path, dimension_name, unit_name, relative_to.value());
|
||||
is_valid = false;
|
||||
}
|
||||
} else {
|
||||
warnln("{}: {} unit `{}` is marked as relative, but only relative length units are currently supported.", json_path, dimension_name, unit_name);
|
||||
is_valid = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Must have a canonical unit.
|
||||
if (!canonical_unit.has_value()) {
|
||||
warnln("{}: {} has no unit marked as canonical. Must have exactly 1.", json_path, dimension_name);
|
||||
is_valid = false;
|
||||
}
|
||||
});
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
ErrorOr<void> generate_header_file(JsonObject& dimensions_data, Core::File& file)
|
||||
{
|
||||
StringBuilder builder;
|
||||
SourceGenerator generator { builder };
|
||||
|
||||
generator.append(R"~~~(
|
||||
#pragma once
|
||||
|
||||
#include <AK/Optional.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
)~~~");
|
||||
|
||||
generator.set("enum_type", underlying_type_for_enum(dimensions_data.size()));
|
||||
generator.appendln("enum class DimensionType : @enum_type@ {");
|
||||
dimensions_data.for_each_member([&](auto& name, auto&) {
|
||||
auto dimension_generator = generator.fork();
|
||||
dimension_generator.set("dimension_name:titlecase", title_casify(name));
|
||||
dimension_generator.appendln(" @dimension_name:titlecase@,");
|
||||
});
|
||||
generator.append(R"~~~(
|
||||
};
|
||||
|
||||
Optional<DimensionType> dimension_for_unit(StringView);
|
||||
)~~~");
|
||||
|
||||
dimensions_data.for_each_member([&](auto& dimension_name, auto& value) {
|
||||
auto& units = value.as_object();
|
||||
|
||||
auto enum_generator = generator.fork();
|
||||
enum_generator.set("dimension_name:titlecase", title_casify(dimension_name));
|
||||
enum_generator.set("dimension_name:snakecase", snake_casify(dimension_name));
|
||||
enum_generator.set("enum_type", underlying_type_for_enum(units.size()));
|
||||
|
||||
enum_generator.append(R"~~~(
|
||||
enum class @dimension_name:titlecase@Unit : @enum_type@ {
|
||||
)~~~");
|
||||
units.for_each_member([&](auto& unit_name, auto&) {
|
||||
auto unit_generator = enum_generator.fork();
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.appendln(" @unit_name:titlecase@,");
|
||||
});
|
||||
enum_generator.append(R"~~~(
|
||||
};
|
||||
Optional<@dimension_name:titlecase@Unit> string_to_@dimension_name:snakecase@_unit(StringView);
|
||||
StringView to_string(@dimension_name:titlecase@Unit);
|
||||
double ratio_between_units(@dimension_name:titlecase@Unit, @dimension_name:titlecase@Unit);
|
||||
)~~~");
|
||||
});
|
||||
|
||||
generator.append(R"~~~(
|
||||
bool is_absolute(LengthUnit);
|
||||
bool is_font_relative(LengthUnit);
|
||||
bool is_viewport_relative(LengthUnit);
|
||||
inline bool is_relative(LengthUnit unit) { return !is_absolute(unit); }
|
||||
|
||||
}
|
||||
)~~~");
|
||||
|
||||
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> generate_implementation_file(JsonObject& dimensions_data, Core::File& file)
|
||||
{
|
||||
StringBuilder builder;
|
||||
SourceGenerator generator { builder };
|
||||
|
||||
generator.append(R"~~~(
|
||||
#include <AK/StringView.h>
|
||||
#include <LibWeb/CSS/Units.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
Optional<DimensionType> dimension_for_unit(StringView unit_name)
|
||||
{
|
||||
)~~~");
|
||||
dimensions_data.for_each_member([&](String const& dimension_name, JsonValue const& units) {
|
||||
auto dimension_generator = generator.fork();
|
||||
dimension_generator.set("dimension_name:titlecase", title_casify(dimension_name));
|
||||
dimension_generator.append(" if (");
|
||||
bool first = true;
|
||||
units.as_object().for_each_member([&](String const& unit_name, auto const&) {
|
||||
auto unit_generator = dimension_generator.fork();
|
||||
unit_generator.set("unit_name", unit_name);
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
unit_generator.append("\n || ");
|
||||
unit_generator.append("unit_name.equals_ignoring_ascii_case(\"@unit_name@\"sv)");
|
||||
});
|
||||
dimension_generator.append(R"~~~()
|
||||
return DimensionType::@dimension_name:titlecase@;
|
||||
)~~~");
|
||||
});
|
||||
|
||||
generator.append(R"~~~(
|
||||
return {};
|
||||
}
|
||||
)~~~");
|
||||
|
||||
dimensions_data.for_each_member([&](String const& dimension_name, JsonValue const& dimension_data) {
|
||||
auto& units = dimension_data.as_object();
|
||||
|
||||
String canonical_unit;
|
||||
units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) {
|
||||
if (unit_value.as_object().get_bool("is-canonical-unit"sv) == true)
|
||||
canonical_unit = unit_name;
|
||||
});
|
||||
|
||||
auto dimension_generator = generator.fork();
|
||||
dimension_generator.set("dimension_name:titlecase", title_casify(dimension_name));
|
||||
dimension_generator.set("dimension_name:snakecase", snake_casify(dimension_name));
|
||||
dimension_generator.set("canonical_unit:titlecase", title_casify(canonical_unit));
|
||||
|
||||
dimension_generator.append(R"~~~(
|
||||
Optional<@dimension_name:titlecase@Unit> string_to_@dimension_name:snakecase@_unit(StringView unit_name)
|
||||
{
|
||||
)~~~");
|
||||
units.for_each_member([&](String const& unit_name, JsonValue const&) {
|
||||
auto unit_generator = dimension_generator.fork();
|
||||
unit_generator.set("unit_name:lowercase", unit_name);
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.append(R"~~~(
|
||||
if (unit_name.equals_ignoring_ascii_case("@unit_name:lowercase@"sv))
|
||||
return @dimension_name:titlecase@Unit::@unit_name:titlecase@;)~~~");
|
||||
});
|
||||
|
||||
dimension_generator.append(R"~~~(
|
||||
return {};
|
||||
}
|
||||
|
||||
StringView to_string(@dimension_name:titlecase@Unit value)
|
||||
{
|
||||
switch (value) {)~~~");
|
||||
|
||||
units.for_each_member([&](String const& unit_name, JsonValue const&) {
|
||||
auto unit_generator = dimension_generator.fork();
|
||||
unit_generator.set("unit_name:lowercase", unit_name);
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.append(R"~~~(
|
||||
case @dimension_name:titlecase@Unit::@unit_name:titlecase@:
|
||||
return "@unit_name:lowercase@"sv;)~~~");
|
||||
});
|
||||
|
||||
dimension_generator.append(R"~~~(
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
double ratio_between_units(@dimension_name:titlecase@Unit from, @dimension_name:titlecase@Unit to)
|
||||
{
|
||||
if (from == to)
|
||||
return 1;
|
||||
|
||||
auto ratio_to_canonical_unit = [](@dimension_name:titlecase@Unit unit) -> double {
|
||||
switch (unit) {
|
||||
)~~~");
|
||||
units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) {
|
||||
auto const& unit = unit_value.as_object();
|
||||
if (unit.has("relative-to"sv))
|
||||
return;
|
||||
auto unit_generator = dimension_generator.fork();
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
if (auto ratio = unit.get_double_with_precision_loss("number-of-canonical-unit"sv); ratio.has_value()) {
|
||||
unit_generator.set("unit_ratio", String::number(ratio.value()));
|
||||
} else {
|
||||
// This must be the canonical unit, so the ratio is 1.
|
||||
unit_generator.set("unit_ratio", "1");
|
||||
}
|
||||
unit_generator.append(R"~~~(
|
||||
case @dimension_name:titlecase@Unit::@unit_name:titlecase@:
|
||||
return @unit_ratio@;
|
||||
)~~~");
|
||||
});
|
||||
dimension_generator.append(R"~~~(
|
||||
default:
|
||||
// `from` is a relative unit, so this isn't valid.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
};
|
||||
|
||||
if (to == @dimension_name:titlecase@Unit::@canonical_unit:titlecase@)
|
||||
return ratio_to_canonical_unit(from);
|
||||
return ratio_to_canonical_unit(from) / ratio_to_canonical_unit(to);
|
||||
}
|
||||
)~~~");
|
||||
});
|
||||
|
||||
// And now some length-specific functions.
|
||||
auto& length_units = dimensions_data.get_object("length"sv).value();
|
||||
|
||||
generator.append(R"~~~(
|
||||
bool is_absolute(LengthUnit unit)
|
||||
{
|
||||
switch (unit) {
|
||||
)~~~");
|
||||
length_units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) {
|
||||
auto& unit = unit_value.as_object();
|
||||
if (unit.has("relative-to"sv))
|
||||
return;
|
||||
auto unit_generator = generator.fork();
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.appendln(" case LengthUnit::@unit_name:titlecase@:");
|
||||
});
|
||||
generator.append(R"~~~(
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_font_relative(LengthUnit unit)
|
||||
{
|
||||
switch (unit) {
|
||||
)~~~");
|
||||
length_units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) {
|
||||
auto& unit = unit_value.as_object();
|
||||
if (unit.get_string("relative-to"sv) != "font"sv)
|
||||
return;
|
||||
auto unit_generator = generator.fork();
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.appendln(" case LengthUnit::@unit_name:titlecase@:");
|
||||
});
|
||||
generator.append(R"~~~(
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_viewport_relative(LengthUnit unit)
|
||||
{
|
||||
switch (unit) {
|
||||
)~~~");
|
||||
length_units.for_each_member([&](String const& unit_name, JsonValue const& unit_value) {
|
||||
auto& unit = unit_value.as_object();
|
||||
if (unit.get_string("relative-to"sv) != "viewport"sv)
|
||||
return;
|
||||
auto unit_generator = generator.fork();
|
||||
unit_generator.set("unit_name:titlecase", title_casify(unit_name));
|
||||
unit_generator.appendln(" case LengthUnit::@unit_name:titlecase@:");
|
||||
});
|
||||
generator.append(R"~~~(
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
)~~~");
|
||||
|
||||
TRY(file.write_until_depleted(generator.as_string_view().bytes()));
|
||||
return {};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user