srctree

Robin Linden parent 57f74b25 8b46bf03
css: Implement prefers-color-scheme media queries

inlinesplit
css/media_query.h added: 66, removed: 3, total 63
@@ -10,6 +10,7 @@
 
#include <cassert>
#include <charconv>
#include <cstdint>
#include <iterator>
#include <limits>
#include <optional>
@@ -19,6 +20,12 @@
#include <variant>
 
namespace css {
 
enum class ColorScheme : std::uint8_t {
Light,
Dark,
};
 
// This namespace is a workaround required when using libstdc++.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
namespace detail {
@@ -27,6 +34,7 @@ namespace detail {
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
struct Context {
int window_width{};
ColorScheme color_scheme{ColorScheme::Light};
};
 
struct False {
@@ -34,6 +42,14 @@ struct False {
constexpr bool evaluate(Context const &) const { return false; }
};
 
// https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme
struct PrefersColorScheme {
ColorScheme color_scheme{};
[[nodiscard]] bool operator==(PrefersColorScheme const &) const = default;
 
constexpr bool evaluate(Context const &ctx) const { return ctx.color_scheme == color_scheme; }
};
 
struct Width {
int min{};
int max{std::numeric_limits<int>::max()};
@@ -48,9 +64,10 @@ class MediaQuery {
public:
using Context = detail::Context;
using False = detail::False;
using PrefersColorScheme = detail::PrefersColorScheme;
using Width = detail::Width;
 
using Query = std::variant<False, Width>;
using Query = std::variant<False, PrefersColorScheme, Width>;
Query query{};
[[nodiscard]] bool operator==(MediaQuery const &) const = default;
 
@@ -83,6 +100,18 @@ public:
return parse_width(feature_name, value_str);
}
 
if (feature_name == "prefers-color-scheme") {
if (value_str == "light") {
return MediaQuery{PrefersColorScheme{.color_scheme = ColorScheme::Light}};
}
 
if (value_str == "dark") {
return MediaQuery{PrefersColorScheme{.color_scheme = ColorScheme::Dark}};
}
 
return std::nullopt;
}
 
return std::nullopt;
}
 
@@ -129,6 +158,10 @@ constexpr std::string to_string(MediaQuery::False const &) {
return "false";
}
 
constexpr std::string to_string(MediaQuery::PrefersColorScheme const &q) {
return q.color_scheme == ColorScheme::Light ? "prefers-color-scheme: light" : "prefers-color-scheme: dark";
}
 
constexpr std::string to_string(MediaQuery const &query) {
return std::visit([](auto const &q) { return to_string(q); }, query.query);
}
 
css/media_query_test.cpp added: 66, removed: 3, total 63
@@ -49,6 +49,11 @@ void to_string_tests(etest::Suite &s) {
"300 <= width <= 300");
});
 
s.add_test("to_string: prefers-color-scheme", [](etest::IActions &a) {
a.expect_eq(css::to_string(css::MediaQuery::PrefersColorScheme{.color_scheme = css::ColorScheme::Light}), //
"prefers-color-scheme: light");
});
 
s.add_test("to_string: width", [](etest::IActions &a) {
a.expect_eq(css::to_string(css::MediaQuery::Width{.min = 299, .max = 301}), //
"299 <= width <= 301");
@@ -65,6 +70,30 @@ void false_tests(etest::Suite &s) {
});
}
 
void prefers_color_scheme_tests(etest::Suite &s) {
s.add_test("prefers-color-scheme: light", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("(prefers-color-scheme: light)"),
css::MediaQuery{css::MediaQuery::PrefersColorScheme{.color_scheme = css::ColorScheme::Light}});
 
auto query = css::MediaQuery::PrefersColorScheme{.color_scheme = css::ColorScheme::Light};
a.expect(query.evaluate({.color_scheme = css::ColorScheme::Light}));
a.expect(!query.evaluate({.color_scheme = css::ColorScheme::Dark}));
});
 
s.add_test("prefers-color-scheme: dark", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("(prefers-color-scheme: dark)"),
css::MediaQuery{css::MediaQuery::PrefersColorScheme{.color_scheme = css::ColorScheme::Dark}});
 
auto query = css::MediaQuery::PrefersColorScheme{.color_scheme = css::ColorScheme::Dark};
a.expect(!query.evaluate({.color_scheme = css::ColorScheme::Light}));
a.expect(query.evaluate({.color_scheme = css::ColorScheme::Dark}));
});
 
s.add_test("prefers-color-scheme: invalid-value", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("(prefers-color-scheme: invalid)"), std::nullopt); //
});
}
 
void width_tests(etest::Suite &s) {
s.add_test("width: width", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("(width: 300px)"),
@@ -101,6 +130,7 @@ int main() {
parser_tests(s);
to_string_tests(s);
false_tests(s);
prefers_color_scheme_tests(s);
width_tests(s);
return s.run();
}