ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Style.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <Objectively.h>
29
30#include "Style.h"
31
32#define _Class _Style
33
34#pragma mark - Object
35
39static void dealloc(Object *self) {
40
41 Style *this = (Style *) self;
42
43 release(this->attributes);
44 release(this->selectors);
45
46 super(Object, self, dealloc);
47}
48
52static String *description(const Object *self) {
53
54 const Style *this = (Style *) self;
55
56 return $((Array *) this->selectors, componentsJoinedByCharacters, ", ");
57}
58
62static int hash(const Object *self) {
63
64 Style *this = (Style *) self;
65
66 int hash = HASH_SEED;
67
68 hash = HashForObject(hash, this->attributes);
69 hash = HashForObject(hash, this->selectors);
70
71 return hash;
72}
73
77static bool isEqual(const Object *self, const Object *other) {
78
79 if (super(Object, self, isEqual, other)) {
80 return true;
81 }
82
83 if (other && $(other, isKindOfClass, _Style())) {
84
85 const Style *this = (Style *) self;
86 const Style *that = (Style *) other;
87
88 if ($((Object *) this->selectors, isEqual, (Object *) that->selectors)) {
89 return $((Object *) this->attributes, isEqual, (Object *) that->attributes);
90 }
91 }
92
93 return false;
94}
95
96#pragma mark - Style
97
102static void addAttribute(Style *self, const char *attr, ident value) {
103
104 assert(attr);
105 assert(value);
106
107 char *key = strtrim(attr);
108 assert(key);
109
110 $((Dictionary *) self->attributes, setObjectForKeyPath, value, key);
111
112 free(key);
113}
114
119static void addAttributes(Style *self, const Dictionary *attributes) {
120
121 assert(attributes);
122
123 $((Dictionary *) self->attributes, addEntriesFromDictionary, attributes);
124}
125
130static void addBoolAttribute(Style *self, const char *attr, bool value) {
131 $(self, addAttribute, attr, $$(Boole, valueof, value));
132}
133
138static void addCharactersAttribute(Style *self, const char *attr, const char *value) {
139
140 String *string = $$(String, stringWithCharacters, value);
141
142 $(self, addAttribute, attr, string);
143
144 release(string);
145}
146
151static void addColorAttribute(Style *self, const char *attr, const SDL_Color *color) {
152
153 Number *r = $$(Number, numberWithValue, color->r);
154 Number *g = $$(Number, numberWithValue, color->g);
155 Number *b = $$(Number, numberWithValue, color->b);
156 Number *a = $$(Number, numberWithValue, color->a);
157
158 Array *array = $$(Array, arrayWithObjects, r, g, b, a, NULL);
159
160 $(self, addAttribute, attr, array);
161
162 release(array);
163 release(r);
164 release(g);
165 release(b);
166 release(a);
167}
168
173static void addDoubleAttribute(Style *self, const char *attr, double value) {
174
175 Number *number = $$(Number, numberWithValue, value);
176
177 $(self, addAttribute, attr, number);
178
179 release(number);
180}
181
186static void addEnumAttribute(Style *self, const char *attr, const EnumName *names, int value) {
187
188 String *string = nameof(names, value);
189
190 $(self, addAttribute, attr, string);
191
192 release(string);
193}
194
199static void addFloatAttribute(Style *self, const char *attr, float value) {
200 $(self, addDoubleAttribute, attr, value);
201}
202
207static void addIntegerAttribute(Style *self, const char *attr, int value) {
208 $(self, addDoubleAttribute, attr, value);
209}
210
215static void addPointAttribute(Style *self, const char *attr, const SDL_Point *value) {
216
217 Number *x = $$(Number, numberWithValue, value->x);
218 Number *y = $$(Number, numberWithValue, value->y);
219
220 Array *array = $$(Array, arrayWithObjects, x, y, NULL);
221
222 $(self, addAttribute, attr, array);
223
224 release(array);
225 release(x);
226 release(y);
227}
228
233static void addRectangleAttribute(Style *self, const char *attr, const SDL_Rect *value) {
234
235 Number *x = $$(Number, numberWithValue, value->x);
236 Number *y = $$(Number, numberWithValue, value->y);
237 Number *w = $$(Number, numberWithValue, value->w);
238 Number *h = $$(Number, numberWithValue, value->h);
239
240 Array *array = $$(Array, arrayWithObjects, x, y, w, h, NULL);
241
242 $(self, addAttribute, attr, array);
243
244 release(array);
245 release(x);
246 release(y);
247 release(w);
248 release(h);
249}
250
255static void addSelector(Style *self, Selector *selector) {
256
257 assert(selector);
258
259 $((Array *) self->selectors, addObject, selector);
260}
261
266static void addSizeAttribute(Style *self, const char *attr, const SDL_Size *value) {
267
268 Number *w = $$(Number, numberWithValue, value->w);
269 Number *h = $$(Number, numberWithValue, value->h);
270
271 Array *array = $$(Array, arrayWithObjects, w, h, NULL);
272
273 $(self, addAttribute, attr, array);
274
275 release(array);
276 release(w);
277 release(h);
278}
279
284static ident attributeValue(const Style *self, const char *attr) {
285 return $((Dictionary *) self->attributes, objectForKeyPath, attr);
286}
287
292static Style *initWithAttributes(Style *self, const Dictionary *attributes) {
293
294 self = $(self, initWithRules, NULL);
295 if (self) {
296 if (attributes) {
297 $(self, addAttributes, attributes);
298 }
299 }
300
301 return self;
302}
303
308static Style *initWithRules(Style *self, const char *rules) {
309
310 self = (Style *) super(Object, self, init);
311 if (self) {
312
313 self->selectors = $$(Selector, parse, rules);
314 assert(self->selectors);
315
316 for (size_t i = 0; i < self->selectors->count; i++) {
317 ((Selector *) self->selectors->elements[i])->style = self;
318 }
319
320 self->attributes = (Dictionary *) $$(Dictionary, dictionaryWithCapacity, 4);
321 assert(self->attributes);
322 }
323
324 return self;
325}
326
331static bool isComputedEqual(const Style *self, const Style *other) {
332
333 assert(other);
334
335 if (self->selectors->count == other->selectors->count) {
336
337 for (size_t i = 0; i < self->selectors->count; i++) {
338
339 const Selector *this = $(self->selectors, objectAtIndex, i);
340 const Selector *that = $(other->selectors, objectAtIndex, i);
341
342 if (strcmp(this->rule, that->rule)) {
343 return false;
344 }
345 }
346
347 return true;
348 }
349
350 return false;
351}
352
356static ident parseValue(String *string) {
357
358 ident value = NULL;
359
360 assert(string);
361
362 String *trimmed = $(string, trimmedString);
363 assert(trimmed);
364
365 StringReader *reader = $(alloc(StringReader), initWithString, trimmed);
366 assert(reader);
367
368 const Unicode *charset = L", \n\t";
369 Unicode stop;
370
371 String *token = $(reader, readToken, charset, &stop);
372 if (token) {
373
374 NumberFormatter *formatter = $(alloc(NumberFormatter), initWithFormat, NULL);
375 assert(formatter);
376
377 Number *number = $(formatter, numberFromString, token);
378
379 if (stop == READER_EOF) {
380
381 if (number) {
382 value = retain(number);
383 } else if (strcmp("true", token->chars) == 0) {
384 value = retain($$(Boole, True));
385 } else if (strcmp("false", token->chars) == 0) {
386 value = retain($$(Boole, False));
387 } else {
388 value = $((Object *) token, copy);
389 }
390
391 } else if (number) {
392 value = $$(Array, arrayWithCapacity, 4);
393
394 while (token) {
395
396 Object *obj = parseValue(token);
397 $((Array *) value, addObject, obj);
398 release(obj);
399 release(token);
400
401 token = $(reader, readToken, charset, NULL);
402 }
403 } else {
404 value = retain(trimmed);
405 }
406
407 release(number);
408 release(formatter);
409 release(token);
410 }
411
412 release(reader);
413 release(trimmed);
414
415 return value;
416}
417
422static Array *parse(const char *css) {
423
424 Array *styles = $$(Array, array);
425 assert(styles);
426
427 if (css) {
428
429 StringReader *reader = $(alloc(StringReader), initWithCharacters, css);
430 assert(reader);
431
432 Style *style = NULL;
433 char *attr = NULL;
434
435 while (true) {
436
437 const Unicode *charset = style ? L"{:;}" : L"{}";
438 Unicode stop;
439
440 String *token = $(reader, readToken, charset, &stop);
441 if (token) {
442 switch (stop) {
443 case '{':
444 style = $(alloc(Style), initWithRules, token->chars);
445 assert(style);
446 break;
447 case ':':
448 if (style) {
449 attr = strtrim(token->chars);
450 assert(attr);
451 }
452 break;
453 case ';':
454 if (style && attr) {
455
456 ident value = parseValue(token);
457 $(style, addAttribute, attr, value);
458
459 free(attr);
460 attr = NULL;
461
462 release(value);
463 }
464 break;
465 case '}':
466 if (style) {
467 $(styles, addObject, style);
468 style = release(style);
469 }
470 break;
471 }
472
473 release(token);
474 } else {
475 break;
476 }
477 }
478
479 release(reader);
480 }
481
482 return (Array *) styles;
483}
484
489static void removeAttribute(Style *self, const char *attr) {
490 $((Dictionary *) self->attributes, removeObjectForKeyPath, attr);
491}
492
497static void removeAllAttributes(Style *self) {
498 $((Dictionary *) self->attributes, removeAllObjects);
499}
500
501#pragma mark - Class lifecycle
502
506static void initialize(Class *clazz) {
507
508 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
509 ((ObjectInterface *) clazz->interface)->description = description;
510 ((ObjectInterface *) clazz->interface)->hash = hash;
511 ((ObjectInterface *) clazz->interface)->isEqual = isEqual;
512
513 ((StyleInterface *) clazz->interface)->addAttribute = addAttribute;
514 ((StyleInterface *) clazz->interface)->addAttributes = addAttributes;
515 ((StyleInterface *) clazz->interface)->addBoolAttribute = addBoolAttribute;
516 ((StyleInterface *) clazz->interface)->addCharactersAttribute = addCharactersAttribute;
517 ((StyleInterface *) clazz->interface)->addColorAttribute = addColorAttribute;
518 ((StyleInterface *) clazz->interface)->addDoubleAttribute = addDoubleAttribute;
519 ((StyleInterface *) clazz->interface)->addEnumAttribute = addEnumAttribute;
520 ((StyleInterface *) clazz->interface)->addFloatAttribute = addFloatAttribute;
521 ((StyleInterface *) clazz->interface)->addIntegerAttribute = addIntegerAttribute;
522 ((StyleInterface *) clazz->interface)->addPointAttribute = addPointAttribute;
523 ((StyleInterface *) clazz->interface)->addRectangleAttribute = addRectangleAttribute;
524 ((StyleInterface *) clazz->interface)->addSelector = addSelector;
525 ((StyleInterface *) clazz->interface)->addSizeAttribute = addSizeAttribute;
526 ((StyleInterface *) clazz->interface)->attributeValue = attributeValue;
527 ((StyleInterface *) clazz->interface)->initWithAttributes = initWithAttributes;
528 ((StyleInterface *) clazz->interface)->initWithRules = initWithRules;
529 ((StyleInterface *) clazz->interface)->isComputedEqual = isComputedEqual;
530 ((StyleInterface *) clazz->interface)->parse = parse;
531 ((StyleInterface *) clazz->interface)->removeAllAttributes = removeAllAttributes;
532 ((StyleInterface *) clazz->interface)->removeAttribute = removeAttribute;
533}
534
539Class *_Style(void) {
540 static Class *clazz;
541 static Once once;
542
543 do_once(&once, {
544 clazz = _initialize(&(const ClassDef) {
545 .name = "Style",
546 .superclass = _Object(),
547 .instanceSize = sizeof(Style),
548 .interfaceOffset = offsetof(Style, interface),
549 .interfaceSize = sizeof(StyleInterface),
551 });
552 });
553
554 return clazz;
555}
556
557#undef _Class
static View * init(View *self)
Definition Box.c:67
static void addAttribute(Style *self, const char *attr, ident value)
Definition Style.c:102
static void addDoubleAttribute(Style *self, const char *attr, double value)
Definition Style.c:173
static bool isComputedEqual(const Style *self, const Style *other)
Definition Style.c:331
static void addIntegerAttribute(Style *self, const char *attr, int value)
Definition Style.c:207
static void addEnumAttribute(Style *self, const char *attr, const EnumName *names, int value)
Definition Style.c:186
static bool isEqual(const Object *self, const Object *other)
Definition Style.c:77
static Style * initWithRules(Style *self, const char *rules)
Definition Style.c:308
static Array * parse(const char *css)
Definition Style.c:422
static String * description(const Object *self)
Definition Style.c:52
static void addCharactersAttribute(Style *self, const char *attr, const char *value)
Definition Style.c:138
static void removeAttribute(Style *self, const char *attr)
Definition Style.c:489
static void addSizeAttribute(Style *self, const char *attr, const SDL_Size *value)
Definition Style.c:266
static void removeAllAttributes(Style *self)
Definition Style.c:497
static void addColorAttribute(Style *self, const char *attr, const SDL_Color *color)
Definition Style.c:151
static Style * initWithAttributes(Style *self, const Dictionary *attributes)
Definition Style.c:292
static ident attributeValue(const Style *self, const char *attr)
Definition Style.c:284
static void addRectangleAttribute(Style *self, const char *attr, const SDL_Rect *value)
Definition Style.c:233
static void dealloc(Object *self)
Definition Style.c:39
static void addSelector(Style *self, Selector *selector)
Definition Style.c:255
Class * _Style(void)
Definition Style.c:539
static void addFloatAttribute(Style *self, const char *attr, float value)
Definition Style.c:199
static void initialize(Class *clazz)
Definition Style.c:506
static ident parseValue(String *string)
Definition Style.c:356
static void addBoolAttribute(Style *self, const char *attr, bool value)
Definition Style.c:130
static void addPointAttribute(Style *self, const char *attr, const SDL_Point *value)
Definition Style.c:215
static void addAttributes(Style *self, const Dictionary *attributes)
Definition Style.c:119
static int hash(const Object *self)
Definition Style.c:62
The Style type.
static Stylesheet * initWithString(Stylesheet *self, const String *string)
Definition Stylesheet.c:195
static Stylesheet * initWithCharacters(Stylesheet *self, const char *chars)
Definition Stylesheet.c:124
static Warning * initWithFormat(Warning *self, WarningType type, const char *fmt,...)
Definition Warning.c:50
Selectors are comprised of one or more SelectorSequences.
Definition Selector.h:49
char * rule
The rule, as provided by the user.
Definition Selector.h:70
The Style type.
Definition Style.h:43
Array * selectors
The Selectors.
Definition Style.h:64
Style * initWithRules(Style *self, const char *rules)
Initializes this Style with the given CSS selector rules.
Definition Style.c:308
bool isComputedEqual(const Style *self, const Style *other)
Performs a fast, rule-based comparison of this Style to the given Style.
Definition Style.c:331
Dictionary * attributes
Definition Style.h:59