docs
Loading...
Searching...
No Matches
ComputedStyle.h
Go to the documentation of this file.
1///
2/// Copyright (C) 2026 Ultralight, Inc. All rights reserved.
3/// A license is required for commercial use. https://ultralig.ht
4///
5#pragma once
6#include <Ultralight/Color.h>
9
10#include <cstddef>
11#include <string>
12#include <string_view>
13#include <type_traits>
14#include <utility>
15
16namespace ultralight {
17namespace dom {
18
19/// \cond INTERNAL
20namespace detail {
21
22// One named computed-style member (computed.fontSize). Reads recover the owning
23// ComputedStyle from the member's own address (the StyleProp pattern, over ComputedStyle's
24// standard layout). Distinct type per property name, so members may share one collapsed
25// address. Non-copyable for the same reason as StyleProp: a copy would carry a dangling owner.
26template <FixedString Name>
27struct ComputedStyleProp {
28 ComputedStyleProp() = default;
29 ComputedStyleProp(const ComputedStyleProp&) = delete;
30 ComputedStyleProp& operator=(const ComputedStyleProp&) = delete;
31
32 operator std::string() const;
33
34 StyleValue AsStyleValue() const { return StyleValue::Parse(std::string(*this)); }
35
36 ultralight::Color AsColor() const { return ColorFromStyleText(std::string(*this)); }
37
38 friend bool operator==(const ComputedStyleProp& p, std::string_view v) {
39 return std::string(p) == v;
40 }
41};
42
43// ComputedStyle's handle storage. Public so the member owner recovery can read it; not part
44// of the API.
45struct ComputedStyleStorage {
46 ULDOMElement element = nullptr;
47};
48
49} // namespace detail
50/// \endcond
51
52class ComputedStyle;
53
54///
55/// Get an element's computed style (getComputedStyle).
56///
57/// This is JavaScript's global getComputedStyle(), and the same as Window::getComputedStyle().
58///
59/// @param element The element to read.
60///
61/// @return Returns a read-only view of the element's computed style (see ComputedStyle).
62///
63inline ComputedStyle getComputedStyle(const Element& element);
64
65///
66/// A read-only view of an element's computed style (getComputedStyle).
67///
68/// Holds the values the library uses to render an element after every style rule is applied,
69/// rather than what one rule or the style attribute declares. Get one from
70/// dom::getComputedStyle() (or Window::getComputedStyle()).
71///
72/// ## Reading Values
73///
74/// Every CSS property has a read-only member named like its `el.style` member (eg, `fontSize`,
75/// or `cssFloat` for `float`), and reading one gives you the value as a string. You can also read
76/// a property by name:
77///
78/// ```
79/// dom::ComputedStyle computed = dom::getComputedStyle(badge);
80/// std::string size = computed.fontSize; // eg, "16px"
81/// std::string weight = computed["font-weight"]; // eg, "700" ("fontWeight" works too)
82/// std::string accent = computed.getPropertyValue("--accent");
83/// ```
84///
85/// Custom properties return the value in effect for the element, so you can set one on a
86/// container and read it on any element inside.
87///
88/// ## Typed Reads
89///
90/// Each member's AsStyleValue() parses its value into a dom::StyleValue, and AsColor() parses it
91/// into a Color. A read by name (eg, `computed["--gap"]`) has both too:
92///
93/// ```
94/// if (dom::StyleValue width = computed.width.AsStyleValue())
95/// PlaceMinimap(width.value); // 300 when the width is "300px"
96///
97/// if (Color accent = computed.color.AsColor())
98/// hud_accent = accent;
99/// ```
100///
101/// Lengths come back in pixels (a few properties, such as `border-radius`, keep a percentage) and
102/// times in seconds. A value that isn't a single number (eg, `auto` or a `transform` matrix)
103/// gives an Invalid StyleValue, and an empty read gives an Empty one. AsColor() takes any CSS
104/// color, so it also converts the text of a custom property (eg, `hsl(200 80% 50%)`). A value
105/// that isn't a color gives an invalid Color, and an empty read gives an unset one.
106///
107/// A ComputedStyle is live, so you can keep one and read it again later. Each read returns the
108/// value at the time of the read. Once the element's page is gone, every read returns an empty
109/// string (see dom::Element).
110///
111/// \parblock
112/// @note A read first applies any pending style changes. A property that depends on layout
113/// (eg, `width`, `margin`, `top`, or `transform`) also runs a layout pass, so make all
114/// your changes first and then read.
115/// \endparblock
116///
117/// \parblock
118/// @note The members can't be copied out (`auto w = computed.width` doesn't compile). Read one
119/// into a `std::string`, or call AsStyleValue() or AsColor().
120/// \endparblock
121///
123 public:
124 // One read-only member per CSS property and legacy alias, named like the el.style members
125 // (see the class notes). Generated from the same table as the style proxy.
126#define ULTRALIGHT_DOM_V(ident, cssname) \
127 ULTRALIGHT_DOM_NO_UNIQUE_ADDRESS detail::ComputedStyleProp<cssname> ident;
128 ULDOM_FOR_EACH_CSS_PROPERTY(ULTRALIGHT_DOM_V)
129#undef ULTRALIGHT_DOM_V
130#define ULTRALIGHT_DOM_V(ident, cssname, canonical) \
131 ULTRALIGHT_DOM_NO_UNIQUE_ADDRESS detail::ComputedStyleProp<cssname> ident;
132 ULDOM_FOR_EACH_CSS_PROPERTY_ALIAS(ULTRALIGHT_DOM_V)
133#undef ULTRALIGHT_DOM_V
134
135 ///
136 /// Internal storage (not part of the API).
137 ///
138 detail::ComputedStyleStorage detail_;
139
140 ///
141 /// Create an empty ComputedStyle (every read returns an empty string).
142 ///
144
145 ///
146 /// Copy constructor (both read the same element's style).
147 ///
148 /// @param other The ComputedStyle to copy.
149 ///
151 : detail_ { other.detail_.element ? ulCreateDOMElementRef(other.detail_.element)
152 : nullptr } {}
153
154 ///
155 /// Move constructor (`other` becomes empty).
156 ///
157 /// @param other The ComputedStyle to move from.
158 ///
159 ComputedStyle(ComputedStyle&& other) noexcept : detail_ { other.detail_.element } {
160 other.detail_.element = nullptr;
161 }
162
163 ///
164 /// Assignment (copies or moves).
165 ///
166 /// @param other The ComputedStyle to assign from.
167 ///
168 /// @return Returns this ComputedStyle.
169 ///
171 std::swap(detail_.element, other.detail_.element);
172 return *this;
173 }
174
175 ///
176 /// Destroy this view (the element isn't affected).
177 ///
178 ~ComputedStyle() { ulDestroyDOMElement(detail_.element); }
179
180 ///
181 /// Get a property's computed value (getPropertyValue).
182 ///
183 /// @param name The property name in CSS form (eg, `font-size`, not `fontSize`), or a custom
184 /// property (eg, `--accent`).
185 ///
186 /// @return Returns the computed value, or an empty string if the property is unknown or the
187 /// element isn't in a document.
188 ///
189 std::string getPropertyValue(std::string_view name) const {
190 detail::CString n(name);
191 return detail::TakeString(ulDOMElementGetComputedStyleProperty(detail_.element, n.c_str()));
192 }
193
194 ///
195 /// Get a property's computed value using a name checked at compile time (eg,
196 /// `computed[dom::css<"font-size">]`).
197 ///
198 /// @return Returns the computed value, which reads like a member: it converts to a
199 /// `std::string` and has AsStyleValue() and AsColor().
200 ///
201 /// @see dom::css
202 ///
203 template <detail::FixedString Name>
204 detail::StyleText operator[](CSSPropertyName<Name>) const {
205 return detail::StyleText(detail::TakeString(
206 ulDOMElementGetComputedStyleProperty(detail_.element, CSSPropertyName<Name>::name())));
207 }
208
209 ///
210 /// Get a property's computed value by name.
211 ///
212 /// @param name The property name in CSS form (eg, `font-size`), in camelCase form (eg,
213 /// `fontSize`), or a custom property (eg, `--accent`).
214 ///
215 /// @return Returns the computed value, which reads like a member: it converts to a
216 /// `std::string` and has AsStyleValue() and AsColor().
217 ///
218 detail::StyleText operator[](std::string_view name) const {
219 return detail::StyleText(getPropertyValue(detail::CSSNameOf(name)));
220 }
221
222 private:
223 friend ComputedStyle getComputedStyle(const Element& element);
224 explicit ComputedStyle(ULDOMElement owned_element) : detail_ { owned_element } {}
225};
226
227// The named members recover their owner through offsetof, which is well-defined only on a
228// standard-layout class: every data member is public and declared in this one class.
229static_assert(std::is_standard_layout_v<ComputedStyle>, "ComputedStyle must stay standard-layout");
230static_assert(!std::is_aggregate_v<ComputedStyle>, "ComputedStyle must not be an aggregate");
231
232inline ComputedStyle getComputedStyle(const Element& element) {
233 ULDOMElement handle = element.raw();
234 return ComputedStyle(handle ? ulCreateDOMElementRef(handle) : nullptr);
235}
236
237/// \cond INTERNAL
238namespace detail {
239
240// Named-member offsets, in the same order as kStylePropertyNames (the StyleMemberOffsets()
241// table, over ComputedStyle).
242inline const size_t* ComputedStyleMemberOffsets() {
243 static const size_t offsets[] = {
244#define ULTRALIGHT_DOM_V(ident, cssname) offsetof(ComputedStyle, ident),
245 ULDOM_FOR_EACH_CSS_PROPERTY(ULTRALIGHT_DOM_V)
246#undef ULTRALIGHT_DOM_V
247#define ULTRALIGHT_DOM_V(ident, cssname, canonical) offsetof(ComputedStyle, ident),
248 ULDOM_FOR_EACH_CSS_PROPERTY_ALIAS(ULTRALIGHT_DOM_V)
249#undef ULTRALIGHT_DOM_V
250 };
251 return offsets;
252}
253
254template <FixedString Name>
255ComputedStyleProp<Name>::operator std::string() const {
256 const ComputedStyle* owner = reinterpret_cast<const ComputedStyle*>(
257 reinterpret_cast<const char*>(this) - ComputedStyleMemberOffsets()[kStyleIndex<Name>]);
258 return TakeString(ulDOMElementGetComputedStyleProperty(owner->detail_.element, Name.data));
259}
260
261} // namespace detail
262/// \endcond
263
264} // namespace dom
265} // namespace ultralight
#define ULTRALIGHT_DOM_V(ident, cssname)
Definition ComputedStyle.h:126
bool operator==(const JSRetainPtr< T > &a, const JSRetainPtr< U > &b)
Definition JSRetainPtr.h:169
A read-only view of an element's computed style (getComputedStyle).
Definition ComputedStyle.h:122
~ComputedStyle()
Destroy this view (the element isn't affected).
Definition ComputedStyle.h:178
ComputedStyle(ComputedStyle &&other) noexcept
Move constructor (other becomes empty).
Definition ComputedStyle.h:159
detail::ComputedStyleStorage detail_
Internal storage (not part of the API).
Definition ComputedStyle.h:138
ComputedStyle & operator=(ComputedStyle other) noexcept
Assignment (copies or moves).
Definition ComputedStyle.h:170
ComputedStyle(const ComputedStyle &other)
Copy constructor (both read the same element's style).
Definition ComputedStyle.h:150
ComputedStyle()
Create an empty ComputedStyle (every read returns an empty string).
Definition ComputedStyle.h:143
detail::StyleText operator[](CSSPropertyName< Name >) const
Get a property's computed value using a name checked at compile time (eg, computed[dom::css<"font-siz...
Definition ComputedStyle.h:204
friend ComputedStyle getComputedStyle(const Element &element)
Get an element's computed style (getComputedStyle).
Definition ComputedStyle.h:232
detail::StyleText operator[](std::string_view name) const
Get a property's computed value by name.
Definition ComputedStyle.h:218
std::string getPropertyValue(std::string_view name) const
Get a property's computed value (getPropertyValue).
Definition ComputedStyle.h:189
A handle to an element on a page.
Definition Element.h:142
ULDOMElement raw() const
Get the C handle, for passing to the <Ultralight/CAPI/CAPI_DOMElement.h> functions.
Definition Element.h:2006
@ StyleValue
A CSS number with a unit (see dom::StyleValue).
Definition ValueTraits.h:32
Direct C++ access to modify page elements and handle events.
ComputedStyle getComputedStyle(const Element &element)
Get an element's computed style (getComputedStyle).
Definition ComputedStyle.h:232
Root namespace for every public Ultralight type, function, and enumeration.
A CSS property name checked when your code compiles (the type of dom::css).
Definition CSSPropertyName.h:17
static constexpr const char * name()
Get the property's CSS name (null-terminated).
Definition CSSPropertyName.h:26