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 "MaterialSurfaceStyle.h"
25 : #include "MaterialStyleContainer.h"
26 : #include "MaterialSurfaceInterior.h"
27 :
28 : namespace STAPPLER_VERSIONIZED stappler::xenolith::material2d {
29 :
30 : SurfaceStyle SurfaceStyle::Background = SurfaceStyle{SurfaceStyle::PrimarySchemeTag, ColorRole::Background, Elevation::Level0};
31 :
32 39916 : bool SurfaceStyle::apply(SurfaceStyleData &data, const Size2 &contentSize, const StyleContainer *style, const SurfaceInterior *interior) {
33 39916 : bool dirty = false;
34 :
35 39916 : if (data.schemeTag != schemeTag) {
36 0 : data.schemeTag = schemeTag;
37 0 : dirty = true;
38 : }
39 :
40 : // HCT resolve is expensive, compare only HCT values before it
41 : ColorHCT::Values targetColorHCT, targetColorBackground, targetColorOn;
42 :
43 39916 : float targetElevationValue = 0.0f;
44 39916 : float targetShadowValue = 0.0f;
45 39916 : float targetOutlineValue = 0.0f;
46 :
47 39916 : const ColorScheme *scheme = nullptr;
48 :
49 39916 : if (auto s = style->getScheme(schemeTag)) {
50 39916 : scheme = s;
51 : } else {
52 0 : scheme = &style->getPrimaryScheme();
53 : }
54 :
55 39916 : data.themeType = scheme->type;
56 :
57 39916 : bool hasShadow = false;
58 39916 : bool hasBlendElevation = false;
59 :
60 39916 : auto targetElevation = elevation;
61 :
62 39916 : switch (nodeStyle) {
63 19014 : case NodeStyle::SurfaceTonal:
64 19014 : targetColorHCT = scheme->values(colorRole);
65 19014 : targetColorBackground = scheme->values(ColorRole::Surface);
66 19014 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(ColorRole::Surface, scheme->type));
67 19014 : hasBlendElevation = true;
68 19014 : switch (activityState) {
69 19014 : case ActivityState::Enabled: break;
70 0 : case ActivityState::Disabled:
71 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
72 0 : targetColorBackground = scheme->values(ColorRole::Surface);
73 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
74 0 : targetElevationValue = 0.12f;
75 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
76 0 : break;
77 0 : case ActivityState::Hovered:
78 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
79 0 : targetElevationValue = 0.08f;
80 0 : break;
81 0 : case ActivityState::Focused:
82 0 : targetElevationValue = 0.12f;
83 0 : break;
84 0 : case ActivityState::Pressed:
85 0 : targetElevationValue = 0.12f;
86 0 : break;
87 : }
88 19014 : break;
89 2502 : case NodeStyle::SurfaceTonalElevated:
90 2502 : targetColorHCT = scheme->values(colorRole);
91 2502 : targetColorBackground = scheme->values(ColorRole::Surface);
92 2502 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(ColorRole::Surface, scheme->type));
93 2502 : hasBlendElevation = hasShadow = true;
94 2502 : switch (activityState) {
95 2502 : case ActivityState::Enabled: break;
96 0 : case ActivityState::Disabled:
97 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
98 0 : targetColorBackground = scheme->values(ColorRole::Surface);
99 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
100 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
101 0 : targetElevationValue = 0.12f;
102 0 : break;
103 0 : case ActivityState::Hovered:
104 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
105 0 : targetElevationValue = 0.08f;
106 0 : break;
107 0 : case ActivityState::Focused:
108 0 : targetElevationValue = 0.12f;
109 0 : break;
110 0 : case ActivityState::Pressed:
111 0 : targetElevationValue = 0.12f;
112 0 : break;
113 : }
114 2502 : break;
115 130 : case NodeStyle::Elevated:
116 130 : targetColorHCT = scheme->values(ColorRole::Surface);
117 130 : targetColorBackground = scheme->values(ColorRole::Surface);
118 130 : targetColorOn = scheme->values(colorRole);
119 130 : switch (activityState) {
120 130 : case ActivityState::Enabled: break;
121 0 : case ActivityState::Disabled:
122 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
123 0 : targetColorBackground = scheme->values(ColorRole::Surface);
124 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
125 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
126 0 : targetElevationValue = 0.12f;
127 0 : break;
128 0 : case ActivityState::Hovered:
129 0 : targetColorHCT = scheme->values(ColorRole::Primary);
130 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
131 0 : targetElevationValue = 0.08f;
132 0 : break;
133 0 : case ActivityState::Focused:
134 0 : targetColorHCT = scheme->values(ColorRole::Primary);
135 0 : targetElevationValue = 0.12f;
136 0 : break;
137 0 : case ActivityState::Pressed:
138 0 : targetColorHCT = scheme->values(ColorRole::Primary);
139 0 : targetElevationValue = 0.12f;
140 0 : break;
141 : }
142 130 : hasShadow = true;
143 130 : break;
144 15308 : case NodeStyle::Filled:
145 15308 : targetColorHCT = scheme->values(colorRole);
146 15308 : targetColorBackground = scheme->values(colorRole);
147 15308 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
148 15308 : switch (activityState) {
149 15308 : case ActivityState::Enabled: break;
150 0 : case ActivityState::Disabled:
151 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
152 0 : targetColorBackground = scheme->values(ColorRole::Surface);
153 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
154 0 : targetElevationValue = 0.12f;
155 0 : break;
156 0 : case ActivityState::Hovered:
157 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
158 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
159 0 : targetElevationValue = 0.08f;
160 0 : hasShadow = true;
161 0 : break;
162 0 : case ActivityState::Focused:
163 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
164 0 : targetElevationValue = 0.12f;
165 0 : break;
166 0 : case ActivityState::Pressed:
167 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
168 0 : targetElevationValue = 0.12f;
169 0 : break;
170 : }
171 15308 : break;
172 140 : case NodeStyle::FilledElevated:
173 140 : targetColorHCT = scheme->values(colorRole);
174 140 : targetColorBackground = scheme->values(colorRole);
175 140 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
176 140 : hasShadow = true;
177 140 : switch (activityState) {
178 140 : case ActivityState::Enabled: break;
179 0 : case ActivityState::Disabled:
180 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
181 0 : targetColorBackground = scheme->values(ColorRole::Surface);
182 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
183 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
184 0 : targetElevationValue = 0.12f;
185 0 : break;
186 0 : case ActivityState::Hovered:
187 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
188 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
189 0 : targetElevationValue = 0.08f;
190 0 : break;
191 0 : case ActivityState::Focused:
192 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
193 0 : targetElevationValue = 0.12f;
194 0 : break;
195 0 : case ActivityState::Pressed:
196 0 : targetColorHCT = scheme->values(ColorScheme::getColorRoleOn(colorRole, scheme->type));
197 0 : targetElevationValue = 0.12f;
198 0 : break;
199 : }
200 140 : break;
201 140 : case NodeStyle::FilledTonal:
202 140 : targetColorHCT = scheme->values(ColorRole::SecondaryContainer);
203 140 : targetColorBackground = scheme->values(ColorRole::SecondaryContainer);
204 140 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(ColorRole::SecondaryContainer, scheme->type));
205 140 : switch (activityState) {
206 140 : case ActivityState::Enabled: break;
207 0 : case ActivityState::Disabled:
208 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
209 0 : targetColorBackground = scheme->values(ColorRole::Surface);
210 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
211 0 : targetElevationValue = 0.12f;
212 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
213 0 : break;
214 0 : case ActivityState::Hovered:
215 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
216 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
217 0 : targetElevationValue = 0.08f;
218 0 : break;
219 0 : case ActivityState::Focused:
220 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
221 0 : targetElevationValue = 0.12f;
222 0 : break;
223 0 : case ActivityState::Pressed:
224 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
225 0 : targetElevationValue = 0.12f;
226 0 : break;
227 : }
228 140 : break;
229 90 : case NodeStyle::FilledTonalElevated:
230 90 : targetColorHCT = scheme->values(ColorRole::SecondaryContainer);
231 90 : targetColorBackground = scheme->values(ColorRole::SecondaryContainer);
232 90 : targetColorOn = scheme->values(ColorScheme::getColorRoleOn(ColorRole::SecondaryContainer, scheme->type));
233 90 : hasShadow = true;
234 90 : switch (activityState) {
235 90 : case ActivityState::Enabled: break;
236 0 : case ActivityState::Disabled:
237 0 : targetColorHCT = scheme->values(ColorRole::OnSurface);
238 0 : targetColorBackground = scheme->values(ColorRole::Surface);
239 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
240 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
241 0 : targetElevationValue = 0.12f;
242 0 : break;
243 0 : case ActivityState::Hovered:
244 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
245 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
246 0 : targetElevationValue = 0.08f;
247 0 : break;
248 0 : case ActivityState::Focused:
249 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
250 0 : targetElevationValue = 0.12f;
251 0 : break;
252 0 : case ActivityState::Pressed:
253 0 : targetColorHCT = scheme->values(ColorRole::OnSecondaryContainer);
254 0 : targetElevationValue = 0.12f;
255 0 : break;
256 : }
257 90 : break;
258 20 : case NodeStyle::Outlined:
259 20 : targetColorHCT = scheme->values(ColorRole::Outline, 0.0f);
260 20 : targetColorBackground = scheme->values(ColorRole::Outline, 0.0f);
261 20 : targetColorOn = interior ? interior->getStyle().colorOn.data : scheme->values(colorRole);
262 20 : targetOutlineValue = 1.0f;
263 20 : switch (activityState) {
264 20 : case ActivityState::Enabled: break;
265 0 : case ActivityState::Disabled:
266 0 : targetColorHCT = scheme->values(ColorRole::OnSurface, 0.0f);
267 0 : targetColorBackground = scheme->values(ColorRole::Surface, 0.0f);
268 0 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
269 0 : targetElevation = Elevation(toInt(targetElevation) - 1);
270 0 : targetElevationValue = 0.12f;
271 0 : break;
272 0 : case ActivityState::Hovered:
273 0 : targetElevationValue = 0.08f;
274 0 : targetColorHCT = scheme->values(colorRole, 1.0f);
275 0 : targetElevation = Elevation(toInt(targetElevation) + 1);
276 0 : break;
277 0 : case ActivityState::Focused:
278 0 : targetElevationValue = 0.12f;
279 0 : targetColorHCT = scheme->values(colorRole, 1.0f);
280 0 : break;
281 0 : case ActivityState::Pressed:
282 0 : targetElevationValue = 0.12f;
283 0 : targetColorHCT = scheme->values(colorRole, 1.0f);
284 0 : break;
285 : }
286 20 : break;
287 2572 : case NodeStyle::Text:
288 2572 : targetColorHCT = scheme->values(ColorRole::Surface, 0.0f);
289 2572 : targetColorBackground = scheme->values(colorRole, 0.0f);
290 2572 : targetColorOn = interior ? interior->getStyle().colorOn.data : scheme->values(colorRole);
291 2572 : switch (activityState) {
292 2148 : case ActivityState::Enabled: break;
293 182 : case ActivityState::Disabled:
294 182 : targetColorOn = scheme->values(ColorRole::OnSurface, 0.34f);
295 182 : targetElevation = Elevation(toInt(targetElevation) - 1);
296 182 : break;
297 6 : case ActivityState::Hovered:
298 6 : targetElevationValue = 0.08f;
299 6 : targetColorHCT = scheme->values(ColorRole::Surface, 1.0f);
300 6 : targetColorHCT = scheme->values(colorRole, 1.0f);
301 6 : targetElevation = Elevation(toInt(targetElevation) + 1);
302 6 : break;
303 0 : case ActivityState::Focused:
304 0 : targetElevationValue = 0.12f;
305 0 : targetColorHCT = scheme->values(ColorRole::Surface, 1.0f);
306 0 : targetColorHCT = scheme->values(colorRole, 1.0f);
307 0 : break;
308 236 : case ActivityState::Pressed:
309 236 : targetElevationValue = 0.12f;
310 236 : targetColorHCT = scheme->values(ColorRole::Surface, 1.0f);
311 236 : targetColorHCT = scheme->values(colorRole, 1.0f);
312 236 : break;
313 : }
314 2572 : break;
315 : }
316 :
317 39916 : switch (activityState) {
318 242 : case ActivityState::Focused:
319 : case ActivityState::Hovered:
320 : case ActivityState::Pressed:
321 242 : targetColorOn.alpha = 1.0f;
322 242 : break;
323 182 : case ActivityState::Disabled:
324 182 : targetColorOn.alpha = 0.34f;
325 182 : break;
326 39492 : default:
327 39492 : targetColorOn.alpha = 0.85f;
328 39492 : break;
329 : }
330 :
331 39916 : if (targetColorHCT != data.colorHCT.data) {
332 6228 : data.colorHCT = targetColorHCT;
333 6228 : data.colorScheme = data.colorHCT.asColor4F();
334 6228 : dirty = true;
335 : }
336 39916 : if (targetColorBackground != data.colorBackground.data) {
337 6166 : data.colorBackground = targetColorBackground;
338 6166 : dirty = true;
339 : }
340 39916 : if (targetOutlineValue != data.outlineValue) {
341 10 : data.outlineValue = targetOutlineValue;
342 10 : dirty = true;
343 : }
344 :
345 39916 : if (hasBlendElevation) {
346 21516 : switch (targetElevation) {
347 18504 : case Elevation::Level0: targetElevationValue += 0.0f; break; // 0dp
348 240 : case Elevation::Level1: targetElevationValue += 0.05f; break; // 1dp 5%
349 422 : case Elevation::Level2: targetElevationValue += 0.08f; break; // 3dp 8%
350 1720 : case Elevation::Level3: targetElevationValue += 0.11f; break; // 6dp 11%
351 270 : case Elevation::Level4: targetElevationValue += 0.12f; break; // 8dp 12%
352 360 : case Elevation::Level5: targetElevationValue += 0.14f; break; // 12dp 14%
353 : }
354 : }
355 :
356 39916 : if (hasShadow) {
357 2862 : switch (targetElevation) {
358 140 : case Elevation::Level0: targetShadowValue = 0.0f; break; // 0dp
359 140 : case Elevation::Level1: targetShadowValue = 1.75f; break; // 1dp 5%
360 292 : case Elevation::Level2: targetShadowValue = 3.0f; break; // 3dp 8%
361 1580 : case Elevation::Level3: targetShadowValue = 5.0f; break; // 6dp 11%
362 130 : case Elevation::Level4: targetShadowValue = 7.0f; break; // 8dp 12%
363 580 : case Elevation::Level5: targetShadowValue = 9.0f; break; // 12dp 14%
364 0 : default: targetShadowValue = 10.0f + 3.0f * (toInt(targetElevation) - 5); break;
365 : }
366 : }
367 :
368 39916 : if (targetElevationValue != data.elevationValue) {
369 406 : data.elevationValue = targetElevationValue;
370 406 : dirty = true;
371 : }
372 :
373 39916 : if (dirty) {
374 6448 : data.colorElevation = data.colorBackground.asColor4F() * (1.0f - data.elevationValue) + data.colorScheme * data.elevationValue;
375 : }
376 :
377 39916 : if (targetColorOn != data.colorOn.data) {
378 6228 : data.colorOn = targetColorOn;
379 6228 : dirty = true;
380 : }
381 :
382 39916 : if (targetShadowValue != data.shadowValue) {
383 300 : data.shadowValue = targetShadowValue;
384 300 : dirty = true;
385 : }
386 :
387 39916 : float targetCornerRadius = 0.0f;
388 :
389 39916 : switch (shapeStyle) {
390 36134 : case ShapeStyle::None: targetCornerRadius = 0.0f; break; // 0dp
391 362 : case ShapeStyle::ExtraSmall: targetCornerRadius = 4.0f; break; // 4dp
392 260 : case ShapeStyle::Small: targetCornerRadius = 8.0f; break; // 8dp
393 280 : case ShapeStyle::Medium: targetCornerRadius = 12.0f; break; // 12dp
394 280 : case ShapeStyle::Large: targetCornerRadius = 16.0f; break; // 16dp
395 280 : case ShapeStyle::ExtraLarge: targetCornerRadius = 28.0f; break; // 28dp
396 2320 : case ShapeStyle::Full: targetCornerRadius = std::min(contentSize.width, contentSize.height) / 2.0f; break;
397 : }
398 :
399 39916 : if (data.shapeFamily != shapeFamily) {
400 20 : data.shapeFamily = shapeFamily;
401 20 : dirty = true;
402 : }
403 :
404 39916 : if (targetCornerRadius != data.cornerRadius) {
405 560 : data.cornerRadius = targetCornerRadius;
406 560 : dirty = true;
407 : }
408 :
409 39916 : return dirty;
410 : }
411 :
412 1551 : SurfaceStyleData SurfaceStyleData::progress(const SurfaceStyleData &l, const SurfaceStyleData &r, float p) {
413 1551 : SurfaceStyleData ret(r);
414 1551 : ret.schemeTag = (p < 0.5f) ? l.schemeTag : r.schemeTag;
415 1551 : ret.colorHCT = stappler::progress(l.colorHCT, r.colorHCT, p);
416 1551 : ret.colorBackground = stappler::progress(l.colorBackground, r.colorBackground, p);
417 1551 : ret.colorOn = stappler::progress(l.colorOn, r.colorOn, p);
418 :
419 1551 : ret.colorScheme = ret.colorHCT;
420 1551 : ret.elevationValue = stappler::progress(l.elevationValue, r.elevationValue, p);
421 1551 : ret.outlineValue = stappler::progress(l.outlineValue, r.outlineValue, p);
422 1551 : if (ret.outlineValue < 0.1f) {
423 1551 : ret.outlineValue = 0.0f;
424 : }
425 :
426 1551 : ret.colorElevation = ret.colorBackground.asColor4F() * (1.0f - ret.elevationValue) + ret.colorScheme * ret.elevationValue;
427 :
428 1551 : if (l.shapeFamily == r.shapeFamily) {
429 1551 : ret.cornerRadius = stappler::progress(l.cornerRadius, r.cornerRadius, p);
430 : } else {
431 0 : auto scale = l.cornerRadius / (l.cornerRadius + r.cornerRadius);
432 0 : if (p < scale) {
433 0 : ret.cornerRadius = stappler::progress(l.cornerRadius, 0.0f, p / scale);
434 : } else {
435 0 : ret.cornerRadius = stappler::progress(0.0f, r.cornerRadius, p / (1.0f - scale));
436 : }
437 : }
438 :
439 1551 : ret.shadowValue = stappler::progress(l.shadowValue, r.shadowValue, p);
440 1551 : return ret;
441 : }
442 :
443 : }
|