Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "MaterialSurface.h"
25 : #include "MaterialEasing.h"
26 : #include "MaterialSurfaceInterior.h"
27 : #include "MaterialStyleContainer.h"
28 : #include "XLFrameInfo.h"
29 : #include "XL2dFrameContext.h"
30 : #include "XL2dCommandList.h"
31 :
32 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
33 :
34 588 : bool Surface::init(const SurfaceStyle &style) {
35 588 : if (!VectorSprite::init(Size2(8.0f, 8.0f))) {
36 0 : return false;
37 : }
38 :
39 588 : _interior = addComponent(Rc<SurfaceInterior>::create());
40 :
41 588 : _styleOrigin = _styleTarget = style;
42 588 : _styleDirty = true;
43 :
44 588 : setQuality(QualityHigh);
45 :
46 588 : return true;
47 : }
48 :
49 56 : void Surface::setStyle(const SurfaceStyle &style) {
50 56 : if (_inTransition) {
51 0 : _styleDirty = true;
52 0 : stopAllActionsByTag(TransitionActionTag);
53 0 : _inTransition = false;
54 0 : _styleProgress = 0.0f;
55 : }
56 :
57 56 : if (_styleOrigin != style) {
58 12 : _styleOrigin = _styleTarget = move(style);
59 12 : _styleDirty = true;
60 : }
61 56 : }
62 :
63 707 : void Surface::setStyle(const SurfaceStyle &style, float duration) {
64 707 : if (duration <= 0.0f || !_running) {
65 56 : setStyle(style);
66 56 : return;
67 : }
68 :
69 651 : if (_inTransition || getActionByTag(TransitionActionTag)) {
70 29 : _styleDirty = true;
71 29 : stopAllActionsByTag(TransitionActionTag);
72 29 : _inTransition = false;
73 29 : _styleProgress = 0.0f;
74 : }
75 :
76 651 : if (_styleOrigin != style) {
77 355 : _styleTarget = move(style);
78 355 : runAction(makeEasing(Rc<ActionProgress>::create(duration, [this] (float progress) {
79 2462 : _styleProgress = progress;
80 2462 : _styleDirty = true;
81 2817 : }, [this] {
82 355 : _inTransition = true;
83 700 : }, [this] {
84 345 : _styleOrigin = _styleTarget;
85 345 : _styleDirty = true;
86 345 : _inTransition = false;
87 345 : _styleProgress = 0.0f;
88 345 : })), TransitionActionTag);
89 355 : _styleDirty = true;
90 : }
91 : }
92 :
93 0 : void Surface::setColorRole(ColorRole value) {
94 0 : if (_styleTarget.colorRole != value) {
95 0 : if (_styleOrigin == _styleTarget) {
96 0 : _styleTarget.colorRole = _styleOrigin.colorRole = value;
97 0 : _styleDirty = true;
98 : } else {
99 0 : _styleTarget.colorRole = value;
100 0 : _styleDirty = true;
101 : }
102 : }
103 0 : }
104 :
105 0 : void Surface::setElevation(Elevation value) {
106 0 : if (_styleTarget.elevation != value) {
107 0 : if (_styleOrigin == _styleTarget) {
108 0 : _styleTarget.elevation = _styleOrigin.elevation = value;
109 0 : _styleDirty = true;
110 : } else {
111 0 : _styleTarget.elevation = value;
112 0 : _styleDirty = true;
113 : }
114 : }
115 0 : }
116 :
117 0 : void Surface::setShapeFamily(ShapeFamily value) {
118 0 : if (_styleTarget.shapeFamily != value) {
119 0 : if (_styleOrigin == _styleTarget) {
120 0 : _styleTarget.shapeFamily = _styleOrigin.shapeFamily = value;
121 0 : _styleDirty = true;
122 : } else {
123 0 : _styleTarget.shapeFamily = value;
124 0 : _styleDirty = true;
125 : }
126 : }
127 0 : }
128 :
129 6 : void Surface::setShapeStyle(ShapeStyle value) {
130 6 : if (_styleTarget.shapeStyle != value) {
131 6 : if (_styleOrigin == _styleTarget) {
132 6 : _styleTarget.shapeStyle = _styleOrigin.shapeStyle = value;
133 6 : _styleDirty = true;
134 : } else {
135 0 : _styleTarget.shapeStyle = value;
136 0 : _styleDirty = true;
137 : }
138 : }
139 6 : }
140 :
141 0 : void Surface::setNodeStyle(NodeStyle value) {
142 0 : if (_styleTarget.nodeStyle != value) {
143 0 : if (_styleOrigin == _styleTarget) {
144 0 : _styleTarget.nodeStyle = _styleOrigin.nodeStyle = value;
145 0 : _styleDirty = true;
146 : } else {
147 0 : _styleTarget.nodeStyle = value;
148 0 : _styleDirty = true;
149 : }
150 : }
151 0 : }
152 :
153 0 : void Surface::setActivityState(ActivityState value) {
154 0 : if (_styleTarget.activityState != value) {
155 0 : if (_styleOrigin == _styleTarget) {
156 0 : _styleTarget.activityState = _styleOrigin.activityState = value;
157 0 : _styleDirty = true;
158 : } else {
159 0 : _styleTarget.activityState = value;
160 0 : _styleDirty = true;
161 : }
162 : }
163 0 : }
164 :
165 0 : void Surface::setStyleDirtyCallback(Function<void(const SurfaceStyleData &)> &&cb) {
166 0 : _styleDirtyCallback = move(cb);
167 0 : _styleDirty = true;
168 0 : }
169 :
170 14043 : bool Surface::visitDraw(FrameInfo &frame, NodeFlags parentFlags) {
171 14043 : if (!_visible) {
172 4880 : return false;
173 : }
174 :
175 9163 : auto style = getStyleContainerForFrame(frame);
176 9163 : if (!style) {
177 0 : return false;
178 : }
179 :
180 9163 : if (style) {
181 9163 : if (_styleTarget.apply(_styleDataTarget, _contentSize, style, getSurfaceInteriorForFrame(frame))) {
182 1121 : _styleDirty = true;
183 : }
184 :
185 9163 : if (_styleOrigin.apply(_styleDataOrigin, _contentSize, style, getSurfaceInteriorForFrame(frame))) {
186 1087 : _styleDirty = true;
187 : }
188 : }
189 :
190 9163 : if (_styleDirty || _contentSizeDirty || (_image && _contentSize != _image->getImageSize())) {
191 3014 : if (_styleProgress > 0.0f) {
192 1551 : _styleDataCurrent = progress(_styleDataOrigin, _styleDataTarget, _styleProgress);
193 : } else {
194 1463 : _styleDataCurrent = _styleDataOrigin;
195 : }
196 3014 : applyStyle(style, _styleDataCurrent);
197 3014 : _interior->setStyle(SurfaceStyleData(_styleDataCurrent));
198 : }
199 :
200 9163 : return VectorSprite::visitDraw(frame, parentFlags);
201 : }
202 :
203 10 : Pair<float, float> Surface::getHeightLimits(bool flex) const {
204 10 : return pair(_minHeight, _maxHeight);
205 : }
206 :
207 0 : void Surface::setHeightLimits(float min, float max) {
208 0 : _minHeight = min;
209 0 : _maxHeight = max;
210 0 : }
211 :
212 3014 : void Surface::applyStyle(StyleContainer *, const SurfaceStyleData &style) {
213 3014 : if (style.colorElevation.a == 0.0f && style.outlineValue == 0.0f) {
214 227 : setImage(nullptr);
215 227 : setColor(style.colorElevation, false);
216 227 : setDepthIndex(style.shadowValue);
217 227 : _styleDirty = false;
218 227 : return;
219 : }
220 :
221 2787 : auto radius = std::min(std::min(_contentSize.width / 2.0f, _contentSize.height / 2.0f), style.cornerRadius);
222 :
223 1909 : if (radius != _realCornerRadius || (_image && _contentSize != _image->getImageSize()) || _outlineValue != style.outlineValue
224 4696 : || _fillValue != style.colorElevation.a || style.shapeFamily != _realShapeFamily) {
225 1201 : auto img = Rc<VectorImage>::create(_contentSize);
226 :
227 1201 : updateBackgroundImage(img.get(), style, radius);
228 :
229 1201 : _realShapeFamily = style.shapeFamily;
230 1201 : _realCornerRadius = radius;
231 1201 : _outlineValue = style.outlineValue;
232 1201 : _fillValue = style.colorElevation.a;
233 :
234 1201 : setImage(move(img));
235 1201 : }
236 :
237 2787 : if (_styleDirtyCallback) {
238 0 : _styleDirtyCallback(style);
239 : }
240 :
241 2787 : setColor(style.colorElevation, false);
242 2787 : setDepthIndex(style.shadowValue);
243 2787 : _styleDirty = false;
244 : }
245 :
246 1201 : void Surface::updateBackgroundImage(VectorImage *img, const SurfaceStyleData &style, float radius) {
247 1201 : auto path = img->addPath();
248 1201 : if (radius > 0.0f) {
249 640 : switch (style.shapeFamily) {
250 330 : case ShapeFamily::RoundedCorners:
251 330 : path->openForWriting([&] (vg::PathWriter &writer) {
252 330 : writer.moveTo(0.0f, radius)
253 330 : .arcTo(radius, radius, 0.0f, false, true, radius, 0.0f)
254 330 : .lineTo(_contentSize.width - radius, 0.0f)
255 330 : .arcTo(radius, radius, 0.0f, false, true, _contentSize.width, radius)
256 330 : .lineTo(_contentSize.width, _contentSize.height - radius)
257 330 : .arcTo(radius, radius, 0.0f, false, true, _contentSize.width - radius, _contentSize.height)
258 330 : .lineTo(radius, _contentSize.height)
259 330 : .arcTo(radius, radius, 0.0f, false, true, 0.0f, _contentSize.height - radius)
260 330 : .closePath();
261 330 : });
262 330 : break;
263 310 : case ShapeFamily::CutCorners:
264 310 : path->openForWriting([&] (vg::PathWriter &writer) {
265 310 : writer.moveTo(0.0f, radius)
266 310 : .lineTo(radius, 0.0f)
267 310 : .lineTo(_contentSize.width - radius, 0.0f)
268 310 : .lineTo(_contentSize.width, radius)
269 310 : .lineTo(_contentSize.width, _contentSize.height - radius)
270 310 : .lineTo(_contentSize.width - radius, _contentSize.height)
271 310 : .lineTo(radius, _contentSize.height)
272 310 : .lineTo(0.0f, _contentSize.height - radius)
273 310 : .closePath();
274 310 : });
275 310 : break;
276 : }
277 : } else {
278 561 : path->openForWriting([&] (vg::PathWriter &writer) {
279 561 : writer.moveTo(0.0f, 0.0f)
280 561 : .lineTo(_contentSize.width, 0.0f)
281 561 : .lineTo(_contentSize.width, _contentSize.height)
282 561 : .lineTo(0.0f, _contentSize.height)
283 561 : .closePath();
284 561 : });
285 : }
286 :
287 1201 : path->setAntialiased(false)
288 1201 : .setFillColor(Color::White)
289 1201 : .setFillOpacity(uint8_t(style.colorElevation.a * 255.0f))
290 1201 : .setStyle(vg::DrawStyle::None);
291 :
292 1201 : if (style.colorElevation.a > 0.0f) {
293 1201 : path->setStyle(path->getStyle() | vg::DrawStyle::Fill);
294 : }
295 :
296 1201 : if (style.outlineValue > 0.0f) {
297 0 : path->setStrokeWidth(1.0f)
298 0 : .setStyle(path->getStyle() | vg::DrawStyle::Stroke)
299 0 : .setStrokeColor(Color::White)
300 0 : .setStrokeOpacity(uint8_t(style.outlineValue * 255.0f))
301 0 : .setAntialiased(true);
302 : }
303 1201 : }
304 :
305 9163 : StyleContainer *Surface::getStyleContainerForFrame(FrameInfo &frame) const {
306 9163 : return frame.getComponent<StyleContainer>(StyleContainer::ComponentFrameTag);
307 : }
308 :
309 18326 : SurfaceInterior *Surface::getSurfaceInteriorForFrame(FrameInfo &frame) const {
310 18326 : return frame.getComponent<SurfaceInterior>(SurfaceInterior::ComponentFrameTag);
311 : }
312 :
313 1614 : RenderingLevel Surface::getRealRenderingLevel() const {
314 1614 : auto l = VectorSprite::getRealRenderingLevel();
315 1614 : if (l == RenderingLevel::Transparent) {
316 1352 : l = RenderingLevel::Surface;
317 : }
318 1614 : return l;
319 : }
320 :
321 1521 : void Surface::pushShadowCommands(FrameInfo &frame, NodeFlags flags, const Mat4 &t, SpanView<TransformVertexData> data) {
322 1521 : if (_realCornerRadius > 0.0f) {
323 1171 : FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
324 1171 : handle->shadows->pushSdfGroup(t, handle->getCurrentState(), frame.depthStack.back(), [&, this] (CmdSdfGroup2D &cmd) {
325 1171 : switch (_realShapeFamily) {
326 801 : case ShapeFamily::RoundedCorners:
327 801 : cmd.addRoundedRect2D(Rect(Vec2(0, 0), _contentSize), _realCornerRadius);
328 801 : break;
329 370 : case ShapeFamily::CutCorners: {
330 : Vec2 points[8] = {
331 : Vec2(0.0f, _realCornerRadius),
332 : Vec2(_realCornerRadius, 0.0f),
333 370 : Vec2(_contentSize.width - _realCornerRadius, 0.0f),
334 : Vec2(_contentSize.width, _realCornerRadius),
335 370 : Vec2(_contentSize.width, _contentSize.height - _realCornerRadius),
336 370 : Vec2(_contentSize.width - _realCornerRadius, _contentSize.height),
337 : Vec2(_realCornerRadius, _contentSize.height),
338 370 : Vec2(0.0f, _contentSize.height - _realCornerRadius)
339 370 : };
340 370 : cmd.addPolygon2D(points);
341 370 : break;
342 : }
343 : }
344 1171 : });
345 : } else {
346 350 : FrameContextHandle2d *handle = static_cast<FrameContextHandle2d *>(frame.currentContext);
347 350 : handle->shadows->pushSdfGroup(t, handle->getCurrentState(), frame.depthStack.back(), [&, this] (CmdSdfGroup2D &cmd) {
348 350 : cmd.addRect2D(Rect(Vec2(0, 0), _contentSize));
349 350 : });
350 : }
351 : //VectorSprite::pushShadowCommands(frame, flags, t, data);
352 1521 : }
353 :
354 0 : bool BackgroundSurface::init() {
355 0 : return init(material2d::SurfaceStyle::Background);
356 : }
357 :
358 0 : bool BackgroundSurface::init(const SurfaceStyle &style) {
359 0 : if (!Surface::init(style)) {
360 0 : return false;
361 : }
362 :
363 0 : _styleContainer = addComponent(Rc<StyleContainer>::create());
364 :
365 0 : return true;
366 : }
367 :
368 0 : StyleContainer *BackgroundSurface::getStyleContainerForFrame(FrameInfo &frame) const {
369 0 : return _styleContainer;
370 : }
371 :
372 : }
|