88#include < optional>
99#include < algorithm>
1010#include < cctype>
11+ #include < cmath>
1112
1213#include " Effect.hpp"
1314#include " StyleRule.hpp"
1415#include " Environment.hpp"
16+ #include " Helpers.hpp"
1517
1618namespace margelo ::nitro::cssnitro {
1719
@@ -46,18 +48,73 @@ namespace margelo::nitro::cssnitro {
4648 template <class L , class R >
4749 static bool testMediaComparison (const std::string &op, const L &left, const R &right,
4850 reactnativecss::Effect::GetProxy &get) {
49- auto lhs = toNumber (left, get);
50- auto rhs = toNumber (right, get);
51- if (!lhs.has_value () || !rhs.has_value ()) {
51+ using helpers::lower;
52+ using helpers::toNumber;
53+ using helpers::toString;
54+
55+ // Accessors for environment
56+ auto getWidth = [&]() -> double { return get (reactnativecss::env::windowWidth ()); };
57+ auto getHeight = [&]() -> double { return get (reactnativecss::env::windowHeight ()); };
58+
59+ // Special cases when left is a string keyword
60+ if (auto leftStr = toString (left)) {
61+ const std::string key = lower (*leftStr);
62+
63+ if (key == " min-width" ) {
64+ auto v = toNumber (right);
65+ return v.has_value () && getWidth () >= *v;
66+ }
67+ if (key == " max-width" ) {
68+ auto v = toNumber (right);
69+ return v.has_value () && getWidth () <= *v;
70+ }
71+ if (key == " min-height" ) {
72+ auto v = toNumber (right);
73+ return v.has_value () && getHeight () >= *v;
74+ }
75+ if (key == " max-height" ) {
76+ auto v = toNumber (right);
77+ return v.has_value () && getHeight () <= *v;
78+ }
79+ if (key == " orientation" ) {
80+ auto vStr = toString (right);
81+ const double w = getWidth ();
82+ const double h = getHeight ();
83+ return vStr.has_value () && lower (*vStr) == " landscape" ? (h < w) : (h >= w);
84+ }
85+ }
86+
87+ // For general comparisons, right must be numeric
88+ auto rightValOpt = toNumber (right);
89+ if (!rightValOpt.has_value ()) {
5290 return false ;
5391 }
54- const double a = *lhs;
55- const double b = *rhs;
92+
93+ // Left can be numeric or a string referring to width/height
94+ std::optional<double > leftValOpt = toNumber (left);
95+ if (!leftValOpt.has_value ()) {
96+ if (auto leftStr = toString (left)) {
97+ const std::string key = lower (*leftStr);
98+ if (key == " width" ) {
99+ leftValOpt = getWidth ();
100+ } else if (key == " height" ) {
101+ leftValOpt = getHeight ();
102+ } else {
103+ return false ;
104+ }
105+ } else {
106+ return false ;
107+ }
108+ }
109+
110+ const double a = *leftValOpt;
111+ const double b = *rightValOpt;
112+
56113 if (op == " =" ) return nearlyEqual (a, b);
57114 if (op == " >" ) return a > b;
58- if (op == " >=" ) return a >= b || nearlyEqual (a, b);
115+ if (op == " >=" ) return a > b || nearlyEqual (a, b);
59116 if (op == " <" ) return a < b;
60- if (op == " <=" ) return a <= b || nearlyEqual (a, b);
117+ if (op == " <=" ) return a < b || nearlyEqual (a, b);
61118 return false ;
62119 }
63120
@@ -83,71 +140,6 @@ namespace margelo::nitro::cssnitro {
83140 return std::abs (a - b) <= eps * std::max (1.0 , std::max (std::abs (a), std::abs (b)));
84141 }
85142
86- static std::string lower (std::string s) {
87- std::transform (s.begin (), s.end (), s.begin (),
88- [](unsigned char c) { return (char ) std::tolower (c); });
89- return s;
90- }
91-
92- static std::optional<double >
93- tokenToNumberFromEnv (const std::string &tok, reactnativecss::Effect::GetProxy &get) {
94- const auto t = lower (tok);
95- if (t == " w" || t == " width" || t == " screenwidth" ) {
96- return get (reactnativecss::env::windowWidth ());
97- }
98- if (t == " h" || t == " height" || t == " screenheight" ) {
99- return get (reactnativecss::env::windowHeight ());
100- }
101- if (t == " scale" || t == " dpr" || t == " pixelratio" ) {
102- return get (reactnativecss::env::windowScale ());
103- }
104- if (t == " fontscale" || t == " fs" ) {
105- return get (reactnativecss::env::windowFontScale ());
106- }
107- return std::nullopt ;
108- }
109-
110- static std::optional<double > parseNumberString (const std::string &s) {
111- size_t start = 0 , end = s.size ();
112- while (start < end && std::isspace ((unsigned char ) s[start])) ++start;
113- while (end > start && std::isspace ((unsigned char ) s[end - 1 ])) --end;
114- if (start >= end) return std::nullopt ;
115- size_t i = start;
116- if (s[i] == ' +' || s[i] == ' -' ) ++i;
117- bool dot = false ;
118- while (i < end) {
119- char c = s[i];
120- if (std::isdigit ((unsigned char ) c)) ++i;
121- else if (c == ' .' && !dot) {
122- dot = true ;
123- ++i;
124- }
125- else break ;
126- }
127- if (i == start || (i == start + 1 && (s[start] == ' +' || s[start] == ' -' )))
128- return std::nullopt ;
129- try { return std::stod (std::string{s.begin () + (long ) start, s.begin () + (long ) i}); }
130- catch (...) { return std::nullopt ; }
131- }
132-
133- template <class T >
134- static std::optional<double >
135- toNumber (const T &value, reactnativecss::Effect::GetProxy &get) {
136- using Decayed = std::decay_t <T>;
137- if constexpr (std::is_arithmetic_v<Decayed>) {
138- return static_cast <double >(value);
139- } else if constexpr (std::is_same_v<Decayed, bool >) {
140- return value ? 1.0 : 0.0 ;
141- } else if constexpr (std::is_same_v<Decayed, std::string>) {
142- if (auto env = tokenToNumberFromEnv (value, get)) return env;
143- return parseNumberString (value);
144- } else if constexpr (std::is_same_v<Decayed, const char *>) {
145- return toNumber (std::string (value), get);
146- } else {
147- return std::nullopt ;
148- }
149- }
150-
151143 template <class Cond >
152144 static bool
153145 testMediaConditionDispatch (const Cond &cond, reactnativecss::Effect::GetProxy &get) {
0 commit comments