srctree

Robin Linden parent d1e67540 70c31bc0
css: Support media-type media queries

inlinesplit
css/media_query.h added: 62, removed: 4, total 58
@@ -26,6 +26,11 @@ enum class ColorScheme : std::uint8_t {
Dark,
};
 
enum class MediaType : std::uint8_t {
Print,
Screen,
};
 
// This namespace is a workaround required when using libstdc++.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96645
namespace detail {
@@ -35,6 +40,7 @@ namespace detail {
struct Context {
int window_width{};
ColorScheme color_scheme{ColorScheme::Light};
MediaType media_type{MediaType::Screen};
};
 
struct False {
@@ -55,6 +61,14 @@ struct True {
constexpr bool evaluate(Context const &) const { return true; }
};
 
// https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_types
struct Type {
MediaType type{};
[[nodiscard]] bool operator==(Type const &) const = default;
 
constexpr bool evaluate(Context const &ctx) const { return ctx.media_type == type; }
};
 
struct Width {
int min{};
int max{std::numeric_limits<int>::max()};
@@ -71,15 +85,27 @@ public:
using False = detail::False;
using PrefersColorScheme = detail::PrefersColorScheme;
using True = detail::True;
using Type = detail::Type;
using Width = detail::Width;
 
using Query = std::variant<False, PrefersColorScheme, True, Width>;
using Query = std::variant<False, PrefersColorScheme, True, Type, Width>;
Query query{};
[[nodiscard]] bool operator==(MediaQuery const &) const = default;
 
// https://drafts.csswg.org/mediaqueries/#mq-syntax
static constexpr std::optional<MediaQuery> parse(std::string_view s) {
// We only handle media-in-parens right now.
if (s == "all") {
return MediaQuery{True{}};
}
 
if (s == "print") {
return MediaQuery{Type{.type = MediaType::Print}};
}
 
if (s == "screen") {
return MediaQuery{Type{.type = MediaType::Screen}};
}
 
if (!(s.starts_with('(') && s.ends_with(')'))) {
return std::nullopt;
}
@@ -168,6 +194,10 @@ constexpr std::string to_string(MediaQuery::True const &) {
return "true";
}
 
constexpr std::string to_string(MediaQuery::Type const &q) {
return q.type == MediaType::Print ? "print" : "screen";
}
 
constexpr std::string to_string(MediaQuery::PrefersColorScheme const &q) {
return q.color_scheme == ColorScheme::Light ? "prefers-color-scheme: light" : "prefers-color-scheme: dark";
}
 
css/media_query_test.cpp added: 62, removed: 4, total 58
@@ -54,6 +54,11 @@ void to_string_tests(etest::Suite &s) {
"prefers-color-scheme: light");
});
 
s.add_test("to_string: type", [](etest::IActions &a) {
a.expect_eq(css::to_string(css::MediaQuery::Type{.type = css::MediaType::Print}), "print");
a.expect_eq(css::to_string(css::MediaQuery::Type{.type = css::MediaType::Screen}), "screen");
});
 
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");
@@ -104,6 +109,28 @@ void prefers_color_scheme_tests(etest::Suite &s) {
});
}
 
void type_tests(etest::Suite &s) {
s.add_test("type", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("all"), css::MediaQuery{css::MediaQuery::True{}});
 
a.expect_eq(css::MediaQuery::parse("print"),
css::MediaQuery{css::MediaQuery::Type{.type = css::MediaType::Print}} //
);
a.expect_eq(css::MediaQuery::parse("screen"),
css::MediaQuery{css::MediaQuery::Type{.type = css::MediaType::Screen}} //
);
 
a.expect_eq(css::MediaQuery::parse("asdf"), std::nullopt);
 
using Type = css::MediaQuery::Type;
a.expect(Type{.type = css::MediaType::Print}.evaluate({.media_type = css::MediaType::Print}));
a.expect(!Type{.type = css::MediaType::Print}.evaluate({.media_type = css::MediaType::Screen}));
 
a.expect(Type{.type = css::MediaType::Screen}.evaluate({.media_type = css::MediaType::Screen}));
a.expect(!Type{.type = css::MediaType::Screen}.evaluate({.media_type = css::MediaType::Print}));
});
}
 
void width_tests(etest::Suite &s) {
s.add_test("width: width", [](etest::IActions &a) {
a.expect_eq(css::MediaQuery::parse("(width: 300px)"),
@@ -142,6 +169,7 @@ int main() {
false_tests(s);
true_tests(s);
prefers_color_scheme_tests(s);
type_tests(s);
width_tests(s);
return s.run();
}