ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
View.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for OpenGL, SDL2 and GNU 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 <string.h>
26
27#include <Objectively.h>
28
29#include "Log.h"
30#include "View.h"
31#include "ViewController.h"
32#include "Window.h"
33
34const EnumName ViewAlignmentNames[] = MakeEnumNames(
35 MakeEnumAlias(ViewAlignmentNone, none),
36 MakeEnumAlias(ViewAlignmentTop, top),
37 MakeEnumAlias(ViewAlignmentMiddle, middle),
38 MakeEnumAlias(ViewAlignmentBottom, bottom),
39 MakeEnumAlias(ViewAlignmentLeft, left),
40 MakeEnumAlias(ViewAlignmentCenter, center),
41 MakeEnumAlias(ViewAlignmentRight, right),
42 MakeEnumAlias(ViewAlignmentTopLeft, top-left),
43 MakeEnumAlias(ViewAlignmentTopCenter, top-center),
44 MakeEnumAlias(ViewAlignmentTopRight, top-right),
45 MakeEnumAlias(ViewAlignmentMiddleLeft, middle-left),
46 MakeEnumAlias(ViewAlignmentMiddleCenter, middle-center),
47 MakeEnumAlias(ViewAlignmentMiddleRight, middle-right),
48 MakeEnumAlias(ViewAlignmentBottomLeft, bottom-left),
49 MakeEnumAlias(ViewAlignmentBottomCenter, bottom-center),
50 MakeEnumAlias(ViewAlignmentBottomRight, bottom-right),
51 MakeEnumAlias(ViewAlignmentInternal, internal)
52);
53
54const EnumName ViewAutoresizingNames[] = MakeEnumNames(
55 MakeEnumAlias(ViewAutoresizingNone, none),
56 MakeEnumAlias(ViewAutoresizingWidth, width),
57 MakeEnumAlias(ViewAutoresizingHeight, height),
58 MakeEnumAlias(ViewAutoresizingFill, fill),
59 MakeEnumAlias(ViewAutoresizingFit, fit),
60 MakeEnumAlias(ViewAutoresizingContain, contain)
61);
62
63#define _Class _View
64
65#pragma mark - ObjectInterface
66
70static void dealloc(Object *self) {
71
72 View *this = (View *) self;
73
74 free(this->identifier);
75
76 release(this->classNames);
77 release(this->computedStyle);
78 release(this->style);
79 release(this->stylesheet);
80 release(this->subviews);
81 release(this->warnings);
82
83 this->superview = NULL;
84
85 super(Object, self, dealloc);
86}
87
91static String *description(const Object *self) {
92
93 View *this = (View *) self;
94
95 String *classNames = $((Object *) this->classNames, description);
96 String *description = str("%s@%p %s [%d, %d, %d, %d]",
97 classnameof(self),
98 self,
99 classNames->chars,
100 this->frame.x, this->frame.y, this->frame.w, this->frame.h);
101
102 release(classNames);
103 return description;
104}
105
106#pragma mark - View
107
112static _Bool acceptsFirstResponder(const View *self) {
113 return false;
114}
115
120static void addClassName(View *self, const char *className) {
121
122 if (className) {
123
124 String *string = $$(String, stringWithCharacters, className);
125 assert(string);
126
127 $(self->classNames, addObject, string);
128 release(string);
129
130 $(self, invalidateStyle);
131 }
132}
133
138static void addSubview(View *self, View *subview) {
139 $(self, addSubviewRelativeTo, subview, NULL, ViewPositionAfter);
140}
141
146static void addSubviewRelativeTo(View *self, View *subview, View *other, ViewPosition position) {
147
148 assert(subview);
149 assert(subview != other);
150
151 retain(subview);
152
153 $(subview, removeFromSuperview);
154
155 if (other && other->superview == self) {
156
157 const Array *subviews = (Array *) self->subviews;
158 const ssize_t index = $(subviews, indexOfObject, other);
159
160 if (position == ViewPositionAfter) {
161 if (index == (ssize_t) (subviews->count - 1)) {
162 $(self->subviews, addObject, subview);
163 } else {
164 $(self->subviews, insertObjectAtIndex, subview, index + 1);
165 }
166 } else {
167 $(self->subviews, insertObjectAtIndex, subview, index);
168 }
169 } else {
170 $(self->subviews, addObject, subview);
171 }
172
173 release(subview);
174
175 subview->superview = self;
176
177 $(subview, moveToWindow, self->window);
178
179 $(subview, invalidateStyle);
180
181 self->needsLayout = true;
182}
183
188static View *ancestorWithIdentifier(const View *self, const char *identifier) {
189
190 assert(identifier);
191
192 View *view = (View *) self;
193 while (view) {
194 if (view->identifier) {
195 if (strcmp(identifier, view->identifier) == 0) {
196 return view;
197 }
198 }
199 view = view->superview;
200 }
201
202 return NULL;
203}
204
209static void applyStyle(View *self, const Style *style) {
210
211 assert(style);
212
213 const Inlet inlets[] = MakeInlets(
214 MakeInlet("alignment", InletTypeEnum, &self->alignment, (ident) ViewAlignmentNames),
215 MakeInlet("autoresizing-mask", InletTypeEnum, &self->autoresizingMask, (ident) ViewAutoresizingNames),
216 MakeInlet("background-color", InletTypeColor, &self->backgroundColor, NULL),
217 MakeInlet("border-color", InletTypeColor, &self->borderColor, NULL),
218 MakeInlet("border-width", InletTypeInteger, &self->borderWidth, NULL),
219 MakeInlet("clips-subviews", InletTypeBool, &self->clipsSubviews, NULL),
220 MakeInlet("frame", InletTypeRectangle, &self->frame, NULL),
221 MakeInlet("hidden", InletTypeBool, &self->hidden, NULL),
222 MakeInlet("height", InletTypeInteger, &self->frame.h, NULL),
223 MakeInlet("left", InletTypeInteger, &self->frame.x, NULL),
224 MakeInlet("max-height", InletTypeInteger, &self->maxSize.h, NULL),
225 MakeInlet("max-size", InletTypeSize, &self->maxSize, NULL),
226 MakeInlet("max-width", InletTypeInteger, &self->maxSize.w, NULL),
227 MakeInlet("min-height", InletTypeInteger, &self->minSize.h, NULL),
228 MakeInlet("min-size", InletTypeSize, &self->minSize, NULL),
229 MakeInlet("min-width", InletTypeInteger, &self->minSize.w, NULL),
230 MakeInlet("padding", InletTypeRectangle, &self->padding, NULL),
231 MakeInlet("padding-top", InletTypeInteger, &self->padding.top, NULL),
232 MakeInlet("padding-right", InletTypeInteger, &self->padding.right, NULL),
233 MakeInlet("padding-bottom", InletTypeInteger, &self->padding.bottom, NULL),
234 MakeInlet("padding-left", InletTypeInteger, &self->padding.left, NULL),
235 MakeInlet("top", InletTypeInteger, &self->frame.y, NULL),
236 MakeInlet("width", InletTypeInteger, &self->frame.w, NULL)
237 );
238
239 $(self, bind, inlets, style->attributes);
240}
241
246static void applyTheme(View *self, const Theme *theme) {
247
248 assert(theme);
249
250 Style *computedStyle = $(theme, computeStyle, self);
251 assert(computedStyle);
252
253 if (!$(self->computedStyle, isComputedEqual, computedStyle)) {
254
255 release(self->computedStyle);
256 self->computedStyle = retain(computedStyle);
257
259 $(self, applyStyle, self->computedStyle);
260 }
261
262 release(computedStyle);
263}
264
269static void applyThemeIfNeeded(View *self, const Theme *theme) {
270
271 assert(theme);
272
273 if (self->needsApplyTheme) {
274 $(self, applyTheme, theme);
275 }
276
277 self->needsApplyTheme = false;
278
280}
281
286static void attachStylesheet(View *self, SDL_Window *window) {
287
288 assert(window);
289
290 if (self->stylesheet) {
291 Theme *theme = $$(Theme, theme, window);
292 if (theme) {
293 $(theme, addStylesheet, self->stylesheet);
294 }
295 }
296}
297
302static void awakeWithCharacters(View *self, const char *chars) {
303
304 Data *data = $$(Data, dataWithConstMemory, (uint8_t *) chars, strlen(chars));
305
306 $(self, awakeWithData, data);
307
308 release(data);
309}
310
315static void awakeWithData(View *self, const Data *data) {
316
317 Dictionary *dictionary = $$(JSONSerialization, objectFromData, data, 0);
318
319 $(self, awakeWithDictionary, dictionary);
320
321 release(dictionary);
322}
323
328static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
329
330 assert(dictionary);
331
332 const Inlet inlets[] = MakeInlets(
333 MakeInlet("identifier", InletTypeCharacters, &self->identifier, NULL),
334 MakeInlet("classNames", InletTypeClassNames, &self, NULL),
335 MakeInlet("style", InletTypeStyle, &self, NULL),
336 MakeInlet("subviews", InletTypeSubviews, &self, NULL)
337 );
338
339 $(self, bind, inlets, dictionary);
340}
341
346static void awakeWithResource(View *self, const Resource *resource) {
347
348 assert(resource);
349
350 $(self, awakeWithData, resource->data);
351}
352
357static void awakeWithResourceName(View *self, const char *name) {
358
359 Resource *resource = $$(Resource, resourceWithName, name);
360
361 $(self, awakeWithResource, resource);
362
363 release(resource);
364}
365
370static void becomeFirstResponder(View *self) {
371 $$(View, setFirstResponder, self->window, self);
372}
373
378static _Bool _bind(View *self, const Inlet *inlets, const Dictionary *dictionary) {
379
380 if (inlets) {
381 if (bindInlets(inlets, dictionary)) {
382 self->needsLayout = true;
383 return true;
384 }
385 }
386
387 return false;
388}
389
394static SDL_Rect bounds(const View *self) {
395
396 const SDL_Size size = $(self, size);
397
398 const SDL_Rect bounds = {
399 .x = self->padding.left,
400 .y = self->padding.top,
401 .w = size.w - (self->padding.left + self->padding.right),
402 .h = size.h - (self->padding.top + self->padding.bottom),
403 };
404
405 return bounds;
406}
407
412static void bringSubviewToFront(View *self, View *subview) {
413
414 assert(subview);
415
416 if (subview->superview == self) {
417
418 View *last = $((Array *) self->subviews, lastObject);
419 if (last != subview) {
420 $(self, addSubviewRelativeTo, subview, last, ViewPositionAfter);
421 }
422 }
423}
424
429static SDL_Rect clippingFrame(const View *self) {
430
431 SDL_Rect frame = $(self, renderFrame);
432
433 if (self->borderWidth && self->borderColor.a) {
434 for (int i = 0; i < self->borderWidth; i++) {
435 frame.x -= 1;
436 frame.y -= 1;
437 frame.w += 2;
438 frame.h += 2;
439 }
440 }
441
442 const View *superview = self->superview;
443 while (superview) {
444 if (superview->clipsSubviews) {
445 const SDL_Rect clippingFrame = $(superview, clippingFrame);
446 if (SDL_IntersectRect(&clippingFrame, &frame, &frame) == false) {
447
448 if (MVC_LogEnabled(SDL_LOG_PRIORITY_VERBOSE)) {
449 String *desc = $((Object *) self, description);
450 String *superdesc = $((Object *) superview, description);
451
452 MVC_LogVerbose("%s is clipped by %s\n", desc->chars, superdesc->chars);
453
454 release(desc);
455 release(superdesc);
456 }
457
458 frame.w = frame.h = 0;
459 break;
460 }
461 }
462 superview = superview->superview;
463 }
464
465 return frame;
466}
467
472static _Bool containsPoint(const View *self, const SDL_Point *point) {
473
474 const SDL_Rect frame = $(self, clippingFrame);
475
476 return (_Bool) !!SDL_PointInRect(point, &frame);
477}
478
483static int depth(const View *self) {
484 return (self->superview ? $(self->superview, depth) + 1 : 0);
485}
486
491static View *descendantWithIdentifier(const View *self, const char *identifier) {
492
493 assert(identifier);
494
495 if (self->identifier) {
496 if (strcmp(identifier, self->identifier) == 0) {
497 return (View *) self;
498 }
499 }
500
501 const Array *subviews = (Array *) self->subviews;
502 for (size_t i = 0; i < subviews->count; i++) {
503 const View *subview = $(subviews, objectAtIndex, i);
504 View *descendant = $(subview, descendantWithIdentifier, identifier);
505 if (descendant) {
506 return descendant;
507 }
508 }
509
510 return NULL;
511}
512
517static void detachStylesheet(View *self, SDL_Window *window) {
518
519 assert(window);
520
521 if (self->stylesheet) {
522 Theme *theme = $$(Theme, theme, window);
523 if (theme) {
525 }
526 }
527}
528
533static void didMoveToWindow(View *self, SDL_Window *window) {
534
535 if (window) {
536 $(self, attachStylesheet, window);
537
538 self->needsLayout = true;
539 }
540}
541
546static _Bool didReceiveEvent(const View *self, const SDL_Event *event) {
547
548 if ($(self, isVisible)) {
549
550 SDL_Point point;
551
552 if (event->type == SDL_MOUSEBUTTONDOWN || event->type == SDL_MOUSEBUTTONUP) {
553 point = MakePoint(event->button.x, event->button.y);
554 } else if (event->type == SDL_MOUSEMOTION) {
555 point = MakePoint(event->motion.x, event->motion.y);
556 } else if (event->type == SDL_MOUSEWHEEL) {
557 SDL_GetMouseState(&point.x, &point.y);
558 } else {
559 return false;
560 }
561
562 return $(self, containsPoint, &point);
563 }
564
565 return false;
566}
567
572static void draw(View *self, Renderer *renderer) {
573
574 assert(self->window);
575
576 if (self->hidden == false) {
577
578 $(renderer, drawView, self);
579
580 $(self, enumerateSubviews, (ViewEnumerator) draw, renderer);
581 }
582}
583
588static void enumerate(View *self, ViewEnumerator enumerator, ident data) {
589
590 assert(enumerator);
591
592 enumerator(self, data);
593
594 $(self, enumerateDescendants, enumerator, data);
595}
596
601static void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data) {
602
603 assert(enumerator);
604
605 if (self->superview) {
606 const Array *siblings = (Array *) self->superview->subviews;
607 const ssize_t index = $(siblings, indexOfObject, (const ident) self);
608 if (index > 0) {
609 enumerator($(siblings, objectAtIndex, index - 1), data);
610 }
611 if (index < (ssize_t) (siblings->count - 1)) {
612 enumerator($(siblings, objectAtIndex, index + 1), data);
613 }
614 }
615}
616
621static void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data) {
622
623 assert(enumerator);
624
625 for (View *view = self->superview; view; view = view->superview) {
626 enumerator(view, data);
627 }
628}
629
634static void enumerateSelection(View *self, const char *rule, ViewEnumerator enumerator, ident data) {
635
636 Selector *selector = $(alloc(Selector), initWithRule, rule);
637 assert(selector);
638
639 $(selector, enumerateSelection, self, enumerator, data);
640 release(selector);
641}
642
647static void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data) {
648
649 assert(enumerator);
650
651 const Array *subviews = (Array *) self->subviews;
652 for (size_t i = 0; i < subviews->count; i++) {
653
654 View *subview = $(subviews, objectAtIndex, i);
655 enumerator(subview, data);
656
657 $(subview, enumerateDescendants, enumerator, data);
658 }
659}
660
665static void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data) {
666
667 assert(enumerator);
668
669 if (self->superview) {
670
671 const Array *siblings = (Array *) self->superview->subviews;
672 for (size_t i = 0; i < siblings->count; i++) {
673 View *sibling = $(siblings, objectAtIndex, i);
674 if (sibling != self) {
675 enumerator(sibling, data);
676 }
677 }
678 }
679}
680
685static void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data) {
686
687 assert(enumerator);
688
689 const Array *subviews = (Array *) self->subviews;
690 for (size_t i = 0; i < subviews->count; i++) {
691 enumerator((View *) subviews->elements[i], data);
692 }
693}
694
699static void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data) {
700
701 assert(enumerator);
702
703 if (self->superview) {
704 enumerator(self->superview, data);
705 }
706}
707
712static View *firstResponder(SDL_Window *window) {
713
714 assert(window);
715
716 return SDL_GetWindowData(window, "firstResponder");
717}
718
722static _Bool hasClassName_predicate(const ident obj, ident data) {
723 return strcmp(((String *) obj)->chars, (const char *) data) == 0;
724}
725
730static _Bool hasClassName(const View *self, const char *className) {
731
732 if (className) {
733 return $((Set *) self->classNames, containsObjectMatching, hasClassName_predicate, (ident) className);
734 }
735
736 return false;
737}
738
743static View *hitTest(const View *self, const SDL_Point *point) {
744
745 if (self->hidden == false) {
746
747 if ($(self, containsPoint, point)) {
748
749 const Array *subviews = (Array *) self->subviews;
750 for (size_t i = subviews->count; i; i--) {
751
752 const View *subview = $(subviews, objectAtIndex, i - 1);
753 const View *view = $(subview, hitTest, point);
754 if (view) {
755 return (View *) view;
756 }
757 }
758
759 return (View *) self;
760 }
761 }
762
763 return NULL;
764}
765
770static View *init(View *self) {
771 return $(self, initWithFrame, NULL);
772}
773
778static View *initWithFrame(View *self, const SDL_Rect *frame) {
779
780 self = (View *) super(Object, self, init);
781 if (self) {
782
783 if (frame) {
784 self->frame = *frame;
785 }
786
787 self->classNames = $$(MutableSet, setWithCapacity, 0);
788 assert(self->classNames);
789
790 self->computedStyle = $(alloc(Style), initWithAttributes, NULL);
791 assert(self->computedStyle);
792
793 self->subviews = $$(MutableArray, arrayWithCapacity, 0);
794 assert(self->subviews);
795
796 self->style = $(alloc(Style), initWithAttributes, NULL);
797 assert(self->style);
798
799 self->warnings = $$(MutableArray, arrayWithCapacity, 0);
800 assert(self->warnings);
801
802 self->maxSize = MakeSize(INT32_MAX, INT32_MAX);
803
804 self->needsApplyTheme = true;
805 self->needsLayout = true;
806 }
807
808 return self;
809}
810
814static void invalidateStyle_enumerate(View *view, ident data) {
815 view->needsApplyTheme = true;
816}
817
822static void invalidateStyle(View *self) {
823 $(self, enumerate, invalidateStyle_enumerate, NULL);
824}
825
830static _Bool isContainer(const View *self) {
832}
833
838static _Bool isDescendantOfView(const View *self, const View *view) {
839
840 assert(view);
841
842 while (self) {
843 if (self == view) {
844 return true;
845 }
846 self = self->superview;
847 }
848
849 return false;
850}
851
856static _Bool isFirstResponder(const View *self) {
857
858 if (self->window) {
859 return $$(View, firstResponder, self->window) == self;
860 } else {
861 return false;
862 }
863}
864
869static _Bool isVisible(const View *self) {
870
871 for (const View *view = self; view; view = view->superview) {
872 if (view->hidden) {
873 return false;
874 }
875 }
876
877 return true;
878}
879
883static void layoutIfNeeded_recurse(View *subview, ident data) {
884 $(subview, layoutIfNeeded);
885}
886
891static void layoutIfNeeded(View *self) {
892
893 if (self->needsLayout) {
894 $(self, layoutSubviews);
895
896 if (MVC_LogEnabled(SDL_LOG_PRIORITY_DEBUG)) {
897
898 if (self->hidden == false && self->superview && self->superview->clipsSubviews) {
899
900 const SDL_Rect bounds = $(self, bounds);
901 const SDL_Rect superviewBounds = $(self->superview, bounds);
902
903 if (bounds.x + bounds.w > superviewBounds.w ||
904 bounds.y + bounds.h > superviewBounds.h) {
905
906 String *this = $((Object *) self, description);
907 String *that = $((Object *) self->superview, description);
908
909 MVC_LogDebug("%s exceeds superview bounds %s\n", this->chars, that->chars);
910
911 $(self, warn, "%s exceeds superview bounds %s\n", this->chars, that->chars);
912
913 release(this);
914 release(that);
915 }
916 }
917 }
918 }
919
920 self->needsLayout = false;
921
923}
924
929static void layoutSubviews(View *self) {
930
931 if (self->superview == NULL) {
933
935 SDL_GetWindowSize(self->window, &size.w, &size.h);
936
937 $(self, resize, &size);
938 }
939 }
940
942 $(self, sizeToFit);
943 }
944
946 $(self, sizeToContain);
947 }
948
949 const SDL_Rect bounds = $(self, bounds);
950
951 const Array *subviews = (Array *) self->subviews;
952 for (size_t i = 0; i < subviews->count; i++) {
953
954 View *subview = (View *) $(subviews, objectAtIndex, i);
955
956 SDL_Size subviewSize;
957 if (subview->autoresizingMask & ViewAutoresizingFit) {
958 subviewSize = $(subview, sizeThatFits);
959 } else {
960 subviewSize = $(subview, sizeThatContains);
961 }
962
964 subviewSize.w = bounds.w;
965 }
966
968 subviewSize.h = bounds.h;
969 }
970
971 $(subview, resize, &subviewSize);
972
973 switch (subview->alignment & ViewAlignmentMaskHorizontal) {
975 subview->frame.x = 0;
976 break;
978 subview->frame.x = (bounds.w - subview->frame.w) * 0.5;
979 break;
981 subview->frame.x = bounds.w - subview->frame.w;
982 break;
983 }
984
985 switch (subview->alignment & ViewAlignmentMaskVertical) {
987 subview->frame.y = 0;
988 break;
990 subview->frame.y = (bounds.h - subview->frame.h) * 0.5;
991 break;
993 subview->frame.y = bounds.h - subview->frame.h;
994 break;
995 }
996 }
997}
998
1003static _Bool matchesSelector(const View *self, const SimpleSelector *simpleSelector) {
1004
1005 assert(simpleSelector);
1006
1007 const char *pattern = simpleSelector->pattern;
1008
1009 switch (simpleSelector->type) {
1011 return false;
1012
1014 return true;
1015
1017 const Class *clazz = classForName(pattern);
1018 if (clazz) {
1019 return $((Object *) self, isKindOfClass, clazz);
1020 }
1021 }
1022 break;
1023
1025 return $(self, hasClassName, pattern);
1026
1028 if (self->identifier) {
1029 return strcmp(self->identifier, pattern) == 0;
1030 } else {
1031 return false;
1032 }
1033
1035 if (strcmp("first-child", pattern) == 0) {
1036 if (self->superview) {
1037 return $((Array *) self->superview->subviews, firstObject) == self;
1038 }
1039 } else if (strcmp("last-child", pattern) == 0) {
1040 if (self->superview) {
1041 return $((Array *) self->superview->subviews, lastObject) == self;
1042 }
1043 } else if (strcmp("nth-child(even)", pattern) == 0) {
1044 if (self->superview) {
1045 return ($((Array *) self->superview->subviews, indexOfObject, (ident) self) & 1) == 0;
1046 }
1047 } else if (strcmp("nth-child(odd)", pattern) == 0) {
1048 if (self->superview) {
1049 return $((Array *) self->superview->subviews, indexOfObject, (ident) self) & 1;
1050 }
1051 } else if (strcmp("hover", pattern) == 0) {
1052 SDL_Point point;
1053 SDL_GetMouseState(&point.x, &point.y);
1054 return $(self, containsPoint, &point);
1055 }
1056 break;
1057 }
1058
1059 return false;
1060}
1061
1065static void moveToWindow_recurse(View *subview, ident data) {
1066 $(subview, moveToWindow, data);
1067}
1068
1073static void moveToWindow(View *self, SDL_Window *window) {
1074
1075 if (self->window != window) {
1076
1077 $(self, willMoveToWindow, window);
1078
1079 self->window = window;
1080
1081 $(self, didMoveToWindow, window);
1082
1083 $(self, enumerateSubviews, moveToWindow_recurse, window);
1084 }
1085}
1086
1091static String *path(const View *self) {
1092
1093 MutableArray *parts = $$(MutableArray, array);
1094
1095 const View *view = self;
1096 while (view) {
1097
1098 Array *classNames = $((Set *) view->classNames, allObjects);
1099
1100 String *part;
1101 if (view->identifier) {
1102 part = str("#%s", view->identifier);
1103 } else if (classNames->count) {
1104 part = str(".%s", ((String *) $(classNames, firstObject))->chars);
1105 } else {
1106 part = str("%s", classnameof(view));
1107 }
1108
1109 release(classNames);
1110
1111 $(parts, insertObjectAtIndex, part, 0);
1112 release(part);
1113
1114 if (view->viewController) {
1115 break;
1116 }
1117
1118 view = view->superview;
1119 }
1120
1121 String *path = $((Array *) parts, componentsJoinedByCharacters, " > ");
1122
1123 release(parts);
1124
1125 return path;
1126}
1127
1132static void removeAllClassNames(View *self) {
1133
1134 $(self->classNames, removeAllObjects);
1135
1136 $(self, invalidateStyle);
1137}
1138
1142static void removeAllSubviews_enumerate(const Array *array, ident obj, ident data) {
1143 $((View *) data, removeSubview, obj);
1144}
1145
1150static void removeAllSubviews(View *self) {
1151 $(self->subviews, removeAllObjectsWithEnumerator, removeAllSubviews_enumerate, self);
1152}
1153
1158static void removeClassName(View *self, const char *className) {
1159
1160 if (className) {
1161 String *string = $$(String, stringWithCharacters, className);
1162 assert(string);
1163
1164 $(self->classNames, removeObject, string);
1165 release(string);
1166
1167 $(self, invalidateStyle);
1168 }
1169}
1170
1175static void removeFromSuperview(View *self) {
1176
1177 if (self->superview) {
1178 $(self->superview, removeSubview, self);
1179 }
1180}
1181
1186static void removeSubview(View *self, View *subview) {
1187
1188 assert(subview);
1189
1190 if (subview->superview == self) {
1191
1192 subview->superview = NULL;
1193
1194 $(subview, moveToWindow, NULL);
1195
1196 $(self->subviews, removeObject, subview);
1197
1198 self->needsLayout = true;
1199 }
1200}
1201
1206static void render(View *self, Renderer *renderer) {
1207
1208 assert(self->window);
1209
1210 if ($(self, hasClassName, "breakpoint")) {
1211 SDL_TriggerBreakpoint();
1212 }
1213
1214 if (self->backgroundColor.a) {
1215
1216 $(renderer, setDrawColor, &self->backgroundColor);
1217
1218 const SDL_Rect frame = $(self, renderFrame);
1219 $(renderer, drawRectFilled, &frame);
1220 }
1221
1222 if (self->borderWidth && self->borderColor.a) {
1223
1224 $(renderer, setDrawColor, &self->borderColor);
1225
1226 SDL_Rect frame = $(self, renderFrame);
1227 for (int i = 0; i < self->borderWidth; i++) {
1228
1229 $(renderer, drawRect, &frame);
1230
1231 frame.x -= 1;
1232 frame.y -= 1;
1233 frame.w += 2;
1234 frame.h += 2;
1235 }
1236 }
1237
1238 $(renderer, setDrawColor, &Colors.White);
1239}
1240
1244static void renderDeviceDidReset_recurse(View *subview, ident data) {
1245 $(subview, renderDeviceDidReset);
1246}
1247
1252static void renderDeviceDidReset(View *self) {
1254}
1255
1259static void renderDeviceWillReset_recurse(View *subview, ident data) {
1260 $(subview, renderDeviceWillReset);
1261}
1262
1267static void renderDeviceWillReset(View *self) {
1269}
1270
1275static SDL_Rect renderFrame(const View *self) {
1276
1277 SDL_Rect frame = self->frame;
1278
1279 const View *view = self;
1280 const View *superview = view->superview;
1281 while (superview) {
1282
1283 frame.x += superview->frame.x;
1284 frame.y += superview->frame.y;
1285
1286 if (view->alignment != ViewAlignmentInternal) {
1287 frame.x += superview->padding.left;
1288 frame.y += superview->padding.top;
1289 }
1290
1291 view = superview;
1292 superview = view->superview;
1293 }
1294
1295 return frame;
1296}
1297
1302static void replaceSubview(View *self, View *subview, View *replacement) {
1303
1304 assert(subview);
1305 assert(replacement);
1306
1307 $(self, addSubviewRelativeTo, replacement, subview, ViewPositionAfter);
1308 $(self, removeSubview, subview);
1309}
1310
1315static void resignFirstResponder(View *self) {
1316
1317 if ($(self, isFirstResponder)) {
1318 $$(View, setFirstResponder, self->window, NULL);
1319 }
1320}
1321
1326static void resize(View *self, const SDL_Size *size) {
1327
1328 if (self->frame.w != size->w || self->frame.h != size->h) {
1329
1330 self->frame.w = clamp(size->w, self->minSize.w, self->maxSize.w);
1331 self->frame.h = clamp(size->h, self->minSize.h, self->maxSize.h);
1332
1333 self->needsLayout = true;
1334
1335 if (self->superview) {
1336 self->superview->needsLayout = true;
1337 }
1338 }
1339}
1340
1345static void resolve(View *self, Outlet *outlets) {
1346
1347 if (outlets) {
1348 for (Outlet *outlet = outlets; outlet->identifier; outlet++) {
1349 *outlet->view = $(self, descendantWithIdentifier, outlet->identifier);
1350 assert(*outlet->view);
1351 }
1352 }
1353}
1354
1359static void respondToEvent(View *self, const SDL_Event *event) {
1360
1361 assert(event);
1362
1363 if (self->viewController) {
1364 $(self->viewController, respondToEvent, event);
1365 }
1366
1367 if (self->nextResponder) {
1368 $(self->nextResponder, respondToEvent, event);
1369 } else if (self->superview) {
1370 $(self->superview, respondToEvent, event);
1371 }
1372}
1373
1378static Set *_select(View *self, const char *rule) {
1379
1380 Selector *selector = $(alloc(Selector), initWithRule, rule);
1381 assert(selector);
1382
1383 Set *selection = $(selector, select, self);
1384
1385 release(selector);
1386 return selection;
1387}
1388
1393static void setFirstResponder(SDL_Window *window, View *view) {
1394
1395 assert(window);
1396
1397 if (view) {
1398 assert(view->window == window);
1399 SDL_SetWindowData(window, "firstResponder", view);
1400 if (MVC_LogEnabled(SDL_LOG_PRIORITY_DEBUG)) {
1401 String *desc = $((Object *) view, description);
1402 SDL_LogDebug(LOG_CATEGORY_MVC, "%s: %s\n", __func__, desc->chars);
1403 release(desc);
1404 }
1405 } else {
1406 SDL_SetWindowData(window, "firstResponder", NULL);
1407 SDL_LogDebug(LOG_CATEGORY_MVC, "%s: NULL\n", __func__);
1408 }
1409}
1410
1415static SDL_Size size(const View *self) {
1416 return MakeSize(self->frame.w, self->frame.h);
1417}
1418
1423static SDL_Size sizeThatContains(const View *self) {
1424
1425 const SDL_Size size = $(self, size);
1426 const SDL_Size sizeThatFits = $(self, sizeThatFits);
1427
1428 return MakeSize(max(size.w, sizeThatFits.w), max(size.h, sizeThatFits.h));
1429}
1430
1435static SDL_Size sizeThatFits(const View *self) {
1436
1437 SDL_Size size = $(self, size);
1438
1440 size.w = 0;
1441 }
1442
1444 size.h = 0;
1445 }
1446
1447 if ($(self, isContainer)) {
1448 size = MakeSize(0, 0);
1449
1450 Array *subviews = $(self, visibleSubviews);
1451 for (size_t i = 0; i < subviews->count; i++) {
1452
1453 const View *subview = $(subviews, objectAtIndex, i);
1454 const SDL_Size subviewSize = $(subview, sizeThatContains);
1455
1456 SDL_Point subviewOrigin = MakePoint(0, 0);
1457 switch (subview->alignment) {
1458 case ViewAlignmentNone:
1459 subviewOrigin = MakePoint(subview->frame.x, subview->frame.y);
1460 break;
1461 default:
1462 break;
1463 }
1464
1465 size.w = max(size.w, subviewOrigin.x + subviewSize.w);
1466 size.h = max(size.h, subviewOrigin.y + subviewSize.h);
1467 }
1468
1469 size.w += self->padding.left + self->padding.right;
1470 size.h += self->padding.top + self->padding.bottom;
1471
1472 release(subviews);
1473 }
1474
1475 size.w = clamp(size.w, self->minSize.w, self->maxSize.w);
1476 size.h = clamp(size.h, self->minSize.h, self->maxSize.h);
1477
1478 return size;
1479}
1480
1485static void sizeToContain(View *self) {
1486
1487 const SDL_Size size = $(self, sizeThatContains);
1488
1489 $(self, resize, &size);
1490}
1491
1496static void sizeToFit(View *self) {
1497
1498 const SDL_Size size = $(self, sizeThatFits);
1499
1500 $(self, resize, &size);
1501}
1502
1507static View *subviewWithIdentifier(const View *self, const char *identifier) {
1508
1509 assert(identifier);
1510
1511 const Array *subviews = (Array *) self->subviews;
1512 for (size_t i = 0; i < subviews->count; i++) {
1513
1514 View *subview = $(subviews, objectAtIndex, i);
1515 if (subview->identifier) {
1516
1517 if (strcmp(identifier, subview->identifier) == 0) {
1518 return subview;
1519 }
1520 }
1521 }
1522
1523 return NULL;
1524}
1525
1529static void updateBindings_recurse(View *subview, ident data) {
1530 $(subview, updateBindings);
1531}
1532
1537static void updateBindings(View *self) {
1539}
1540
1545static SDL_Rect viewport(const View *self) {
1546
1547 assert(self->window);
1548
1549 const SDL_Rect frame = $(self, renderFrame);
1550
1551 return MVC_TransformToWindow(self->window, &frame);
1552}
1553
1558static View *viewWithCharacters(const char *chars, Outlet *outlets) {
1559
1560 Data *data = $$(Data, dataWithConstMemory, (ident) chars, strlen(chars));
1561
1562 View *view = $$(View, viewWithData, data, outlets);
1563
1564 release(data);
1565
1566 return view;
1567}
1568
1573static View *viewWithData(const Data *data, Outlet *outlets) {
1574
1575 Dictionary *dictionary = $$(JSONSerialization, objectFromData, data, 0);
1576
1577 View *view = $$(View, viewWithDictionary, dictionary, outlets);
1578
1579 release(dictionary);
1580
1581 return view;
1582}
1583
1588static View *viewWithDictionary(const Dictionary *dictionary, Outlet *outlets) {
1589
1590 View *view = NULL;
1591
1592 BindInlet(&MakeInlet(NULL, InletTypeView, &view, NULL), dictionary);
1593
1594 $(view, resolve, outlets);
1595
1596 return view;
1597}
1598
1603static View *viewWithResource(const Resource *resource, Outlet *outlets) {
1604
1605 assert(resource);
1606
1607 return $$(View, viewWithData, resource->data, outlets);
1608}
1609
1614static View *viewWithResourceName(const char *name, Outlet *outlets) {
1615
1616 Resource *resource = $$(Resource, resourceWithName, name);
1617
1618 View *view = $$(View, viewWithResource, resource, outlets);
1619
1620 release(resource);
1621
1622 return view;
1623}
1624
1628static _Bool visibleSubviews_filter(ident obj, ident data) {
1629
1630 const View *view = (View *) obj;
1631
1632 return view->hidden == false && view->alignment != ViewAlignmentInternal;
1633}
1634
1639static Array *visibleSubviews(const View *self) {
1640 return $((Array *) self->subviews, filteredArray, visibleSubviews_filter, NULL);
1641}
1642
1647static void warn(View *self, const char *fmt, ...) {
1648 va_list args;
1649 va_start(args, fmt);
1650
1651 String *warning = $(alloc(String), initWithVaList, fmt, args);
1652 assert(warning);
1653
1654 va_end(args);
1655
1656 $(self->warnings, addObject, warning);
1657 release(warning);
1658}
1659
1664static void willMoveToWindow(View *self, SDL_Window *window) {
1665
1666 if (self->window) {
1667 $(self, resignFirstResponder);
1668 $(self, detachStylesheet, self->window);
1669 }
1670}
1671
1672#pragma mark - View class methods
1673
1677static void initialize(Class *clazz) {
1678
1679 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
1680 ((ObjectInterface *) clazz->interface)->description = description;
1681
1682 ((ViewInterface *) clazz->interface)->acceptsFirstResponder = acceptsFirstResponder;
1683 ((ViewInterface *) clazz->interface)->addClassName = addClassName;
1684 ((ViewInterface *) clazz->interface)->addSubview = addSubview;
1685 ((ViewInterface *) clazz->interface)->addSubviewRelativeTo = addSubviewRelativeTo;
1686 ((ViewInterface *) clazz->interface)->ancestorWithIdentifier = ancestorWithIdentifier;
1687 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
1688 ((ViewInterface *) clazz->interface)->applyTheme = applyTheme;
1689 ((ViewInterface *) clazz->interface)->applyThemeIfNeeded = applyThemeIfNeeded;
1690 ((ViewInterface *) clazz->interface)->attachStylesheet = attachStylesheet;
1691 ((ViewInterface *) clazz->interface)->awakeWithCharacters = awakeWithCharacters;
1692 ((ViewInterface *) clazz->interface)->awakeWithData = awakeWithData;
1693 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
1694 ((ViewInterface *) clazz->interface)->awakeWithResource = awakeWithResource;
1695 ((ViewInterface *) clazz->interface)->awakeWithResourceName = awakeWithResourceName;
1696 ((ViewInterface *) clazz->interface)->becomeFirstResponder = becomeFirstResponder;
1697 ((ViewInterface *) clazz->interface)->bind = _bind;
1698 ((ViewInterface *) clazz->interface)->bounds = bounds;
1699 ((ViewInterface *) clazz->interface)->bringSubviewToFront = bringSubviewToFront;
1700 ((ViewInterface *) clazz->interface)->clippingFrame = clippingFrame;
1701 ((ViewInterface *) clazz->interface)->containsPoint = containsPoint;
1702 ((ViewInterface *) clazz->interface)->depth = depth;
1703 ((ViewInterface *) clazz->interface)->descendantWithIdentifier = descendantWithIdentifier;
1704 ((ViewInterface *) clazz->interface)->detachStylesheet = detachStylesheet;
1705 ((ViewInterface *) clazz->interface)->didMoveToWindow = didMoveToWindow;
1706 ((ViewInterface *) clazz->interface)->didReceiveEvent = didReceiveEvent;
1707 ((ViewInterface *) clazz->interface)->draw = draw;
1708 ((ViewInterface *) clazz->interface)->enumerate = enumerate;
1709 ((ViewInterface *) clazz->interface)->enumerateAdjacent = enumerateAdjacent;
1710 ((ViewInterface *) clazz->interface)->enumerateAncestors = enumerateAncestors;
1711 ((ViewInterface *) clazz->interface)->enumerateDescendants = enumerateDescendants;
1712 ((ViewInterface *) clazz->interface)->enumerateSelection = enumerateSelection;
1713 ((ViewInterface *) clazz->interface)->enumerateSiblings = enumerateSiblings;
1714 ((ViewInterface *) clazz->interface)->enumerateSubviews = enumerateSubviews;
1715 ((ViewInterface *) clazz->interface)->enumerateSuperview = enumerateSuperview;
1716 ((ViewInterface *) clazz->interface)->firstResponder = firstResponder;
1717 ((ViewInterface *) clazz->interface)->hasClassName = hasClassName;
1718 ((ViewInterface *) clazz->interface)->hitTest = hitTest;
1719 ((ViewInterface *) clazz->interface)->init = init;
1720 ((ViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
1721 ((ViewInterface *) clazz->interface)->invalidateStyle = invalidateStyle;
1722 ((ViewInterface *) clazz->interface)->isContainer = isContainer;
1723 ((ViewInterface *) clazz->interface)->isDescendantOfView = isDescendantOfView;
1724 ((ViewInterface *) clazz->interface)->isFirstResponder = isFirstResponder;
1725 ((ViewInterface *) clazz->interface)->isVisible = isVisible;
1726 ((ViewInterface *) clazz->interface)->layoutIfNeeded = layoutIfNeeded;
1727 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
1728 ((ViewInterface *) clazz->interface)->matchesSelector = matchesSelector;
1729 ((ViewInterface *) clazz->interface)->moveToWindow = moveToWindow;
1730 ((ViewInterface *) clazz->interface)->path = path;
1731 ((ViewInterface *) clazz->interface)->removeAllClassNames = removeAllClassNames;
1732 ((ViewInterface *) clazz->interface)->removeAllSubviews = removeAllSubviews;
1733 ((ViewInterface *) clazz->interface)->removeClassName = removeClassName;
1734 ((ViewInterface *) clazz->interface)->removeFromSuperview = removeFromSuperview;
1735 ((ViewInterface *) clazz->interface)->removeSubview = removeSubview;
1736 ((ViewInterface *) clazz->interface)->render = render;
1737 ((ViewInterface *) clazz->interface)->renderDeviceDidReset = renderDeviceDidReset;
1738 ((ViewInterface *) clazz->interface)->renderDeviceWillReset = renderDeviceWillReset;
1739 ((ViewInterface *) clazz->interface)->renderFrame = renderFrame;
1740 ((ViewInterface *) clazz->interface)->replaceSubview = replaceSubview;
1741 ((ViewInterface *) clazz->interface)->resignFirstResponder = resignFirstResponder;
1742 ((ViewInterface *) clazz->interface)->resize = resize;
1743 ((ViewInterface *) clazz->interface)->resolve = resolve;
1744 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
1745 ((ViewInterface *) clazz->interface)->select = _select;
1746 ((ViewInterface *) clazz->interface)->setFirstResponder = setFirstResponder;
1747 ((ViewInterface *) clazz->interface)->size = size;
1748 ((ViewInterface *) clazz->interface)->sizeThatContains = sizeThatContains;
1749 ((ViewInterface *) clazz->interface)->sizeThatFits = sizeThatFits;
1750 ((ViewInterface *) clazz->interface)->sizeToContain = sizeToContain;
1751 ((ViewInterface *) clazz->interface)->sizeToFit = sizeToFit;
1752 ((ViewInterface *) clazz->interface)->subviewWithIdentifier = subviewWithIdentifier;
1753 ((ViewInterface *) clazz->interface)->updateBindings = updateBindings;
1754 ((ViewInterface *) clazz->interface)->viewport = viewport;
1755 ((ViewInterface *) clazz->interface)->viewWithCharacters = viewWithCharacters;
1756 ((ViewInterface *) clazz->interface)->viewWithData = viewWithData;
1757 ((ViewInterface *) clazz->interface)->viewWithDictionary = viewWithDictionary;
1758 ((ViewInterface *) clazz->interface)->viewWithResource = viewWithResource;
1759 ((ViewInterface *) clazz->interface)->viewWithResourceName = viewWithResourceName;
1760 ((ViewInterface *) clazz->interface)->visibleSubviews = visibleSubviews;
1761 ((ViewInterface *) clazz->interface)->warn = warn;
1762 ((ViewInterface *) clazz->interface)->willMoveToWindow = willMoveToWindow;
1763}
1764
1769Class *_View(void) {
1770 static Class *clazz;
1771 static Once once;
1772
1773 do_once(&once, {
1774 clazz = _initialize(&(const ClassDef) {
1775 .name = "View",
1776 .superclass = _Object(),
1777 .instanceSize = sizeof(View),
1778 .interfaceOffset = offsetof(View, interface),
1779 .interfaceSize = sizeof(ViewInterface),
1781 });
1782 });
1783
1784 return clazz;
1785}
1786
1787#undef _Class
View logging facilities via SDL_Log.
#define MVC_LogVerbose(fmt,...)
Definition: Log.h:43
#define MVC_LogDebug(fmt,...)
Definition: Log.h:46
#define MVC_LogEnabled(priority)
Definition: Log.h:37
#define LOG_CATEGORY_MVC
Definition: Log.h:33
@ SimpleSelectorTypePseudo
@ SimpleSelectorTypeId
@ SimpleSelectorTypeClass
@ SimpleSelectorTypeUniversal
@ SimpleSelectorTypeType
@ SimpleSelectorTypeNone
#define MakeSize(w, h)
Creates an SDL_Size with the given dimensions.
Definition: Types.h:79
void(* ViewEnumerator)(View *view, ident data)
A function type for View enumeration.
Definition: Types.h:55
#define MakePoint(x, y)
Creates an SDL_Point with the given coordinates.
Definition: Types.h:69
_Bool bindInlets(const Inlet *inlets, const Dictionary *dictionary)
Binds each Inlet specified in inlets to the data provided in dictionary.
Definition: View+JSON.c:326
@ InletTypeSubviews
Definition: View+JSON.h:122
@ InletTypeStyle
Definition: View+JSON.h:116
@ InletTypeEnum
Definition: View+JSON.h:75
@ InletTypeCharacters
Definition: View+JSON.h:52
@ InletTypeBool
Definition: View+JSON.h:46
@ InletTypeClassNames
Definition: View+JSON.h:57
@ InletTypeInteger
Definition: View+JSON.h:95
@ InletTypeColor
Definition: View+JSON.h:62
@ InletTypeView
Definition: View+JSON.h:139
@ InletTypeSize
Definition: View+JSON.h:110
@ InletTypeRectangle
Definition: View+JSON.h:105
#define BindInlet(inlet, obj)
Binds the Inlet to obj by invoking the appropriate InletBinding function.
Definition: View+JSON.h:244
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
Definition: View+JSON.h:221
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
Definition: View+JSON.h:216
const EnumName ViewAlignmentNames[]
Definition: View.c:34
static _Bool visibleSubviews_filter(ident obj, ident data)
Predicate for visibleSubviews.
Definition: View.c:1628
static Set * _select(View *self, const char *rule)
Definition: View.c:1378
static void removeAllSubviews_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for removeAllSubviews.
Definition: View.c:1142
static void renderDeviceWillReset_recurse(View *subview, ident data)
ViewEnumerator for renderDeviceWillReset recursion.
Definition: View.c:1259
static _Bool hasClassName_predicate(const ident obj, ident data)
Predicate for hasClassName.
Definition: View.c:722
static String * description(const Object *self)
Definition: View.c:91
static void layoutIfNeeded_recurse(View *subview, ident data)
ViewEnumerator for layoutIfNeeded recursion.
Definition: View.c:883
static _Bool _bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
Definition: View.c:378
static void dealloc(Object *self)
Definition: View.c:70
static void moveToWindow_recurse(View *subview, ident data)
ViewEnumerator for moveToWindow recursion.
Definition: View.c:1065
static void invalidateStyle_enumerate(View *view, ident data)
ViewEnumerator for invalidateStyle.
Definition: View.c:814
static void updateBindings_recurse(View *subview, ident data)
ViewEnumerator for updateBindings recursion.
Definition: View.c:1529
static void initialize(Class *clazz)
Definition: View.c:1677
const EnumName ViewAutoresizingNames[]
Definition: View.c:54
static void renderDeviceDidReset_recurse(View *subview, ident data)
ViewEnumerator for renderDeviceDidReset recursion.
Definition: View.c:1244
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
#define ViewAlignmentMaskBottom
Definition: View.h:45
#define ViewAlignmentMaskTop
Definition: View.h:43
#define ViewAlignmentMaskMiddle
Definition: View.h:44
#define ViewAlignmentMaskHorizontal
Definition: View.h:54
ViewPosition
Relative positioning of subviews within their superview.
Definition: View.h:118
@ ViewPositionAfter
Definition: View.h:120
#define ViewAlignmentMaskVertical
Definition: View.h:51
@ ViewAutoresizingNone
Definition: View.h:86
@ ViewAutoresizingFill
Definition: View.h:89
@ ViewAutoresizingContain
Definition: View.h:91
@ ViewAutoresizingWidth
Definition: View.h:87
@ ViewAutoresizingHeight
Definition: View.h:88
@ ViewAutoresizingFit
Definition: View.h:90
@ ViewAlignmentBottomRight
Definition: View.h:76
@ ViewAlignmentBottomLeft
Definition: View.h:74
@ ViewAlignmentMiddleCenter
Definition: View.h:72
@ ViewAlignmentInternal
Definition: View.h:77
@ ViewAlignmentTopCenter
Definition: View.h:69
@ ViewAlignmentTopRight
Definition: View.h:70
@ ViewAlignmentMiddleRight
Definition: View.h:73
@ ViewAlignmentTopLeft
Definition: View.h:68
@ ViewAlignmentCenter
Definition: View.h:66
@ ViewAlignmentLeft
Definition: View.h:65
@ ViewAlignmentRight
Definition: View.h:67
@ ViewAlignmentTop
Definition: View.h:62
@ ViewAlignmentMiddleLeft
Definition: View.h:71
@ ViewAlignmentBottom
Definition: View.h:64
@ ViewAlignmentMiddle
Definition: View.h:63
@ ViewAlignmentBottomCenter
Definition: View.h:75
@ ViewAlignmentNone
Definition: View.h:61
A ViewController manages a View and its descendants.
SDL_Rect MVC_TransformToWindow(SDL_Window *window, const SDL_Rect *rect)
Transforms the specified rectangle to normalized device coordinates in window.
Definition: Window.c:28
Box * initWithFrame(Box *self, const SDL_Rect *frame)
Initializes this Box with the given frame.
Definition: Box.c:92
CollectionView * initWithFrame(CollectionView *self, const SDL_Rect *frame)
CollectionView * init(CollectionView *self, const SDL_Rect *frame)
Initializes this CollectionView with the specified frame and style.
W3C Color constants.
Definition: Colors.h:37
SDL_Color White
Definition: Colors.h:185
void renderDeviceDidReset(Font *self)
This method should be invoked when the render context is invalidated.
Definition: Font.c:247
SDL_Size size(const Image *self)
Definition: Image.c:181
Inlets enable inbound data binding of View attributes through JSON.
Definition: View+JSON.h:155
Outlets enable outbound data binding of Views through JSON.
Definition: View+JSON.h:200
const char * identifier
The View identifier.
Definition: View+JSON.h:205
The Renderer is responsible for rasterizing the View hierarchy of a WindowController.
Definition: Renderer.h:50
void drawRectFilled(const Renderer *self, const SDL_Rect *rect)
Fills a rectangle using glRecti.
Definition: Renderer.c:144
void drawRect(const Renderer *self, const SDL_Rect *rect)
Draws a rectangle using GL_LINE_LOOP.
Definition: Renderer.c:118
void renderDeviceWillReset(Renderer *self)
This method is invoked when the render device will become reset.
Definition: Renderer.c:252
void drawView(Renderer *self, View *view)
Draws the given View, setting the clipping frame and invoking View::render.
Definition: Renderer.c:195
void setDrawColor(Renderer *self, const SDL_Color *color)
Sets the primary color for drawing operations.
Definition: Renderer.c:281
The SDL_Size type.
Definition: Types.h:62
int w
Definition: Types.h:63
int h
Definition: Types.h:63
Selectors are comprised of one or more SelectorSequences.
Definition: Selector.h:49
Selector * initWithRule(Selector *self, const char *rule)
Initializes this Selector with the given rule.
Definition: Selector.c:188
void enumerateSelection(const Selector *self, View *view, ViewEnumerator enumerator, ident data)
Selects from view and applies the given ViewEnumerator to all matched Views.
Definition: Selector.c:166
Array * select(const Selector *self, View *view)
The SimpleSelector type.
SimpleSelectorType type
The SimpleSelectorType.
char * pattern
The pattern, as provided by the user.
The Style type.
Definition: Style.h:43
Style * initWithAttributes(Style *self, const Dictionary *attributes)
Initializes this Style with the given attributes.
Definition: Style.c:292
_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
void addAttributes(Style *self, const Dictionary *attributes)
Adds or replaces the attribtues in attributes to this Style.
Definition: Style.c:119
The Theme type.
Definition: Theme.h:51
Style * computeStyle(const Theme *self, View *view)
void removeStylesheet(Theme *self, Stylesheet *stylesheet)
Removes the given Stylesheet from this Theme.
Definition: Theme.c:135
void addStylesheet(Theme *self, Stylesheet *stylesheet)
Adds the specified Stylesheet to this Theme.
Definition: Theme.c:55
Theme * theme(SDL_Window *window)
Definition: Theme.c:143
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition: View.h:133
Stylesheet * stylesheet
An optional Stylesheet.
Definition: View.h:247
String * path(const View *self)
Definition: View.c:1091
_Bool hasClassName(const View *self, cosnt char *className)
_Bool isVisible(const View *self)
Definition: View.c:869
void resolve(View *self, Outlet *outlets)
Resolves the given Outlets from this View's hierarchy.
Definition: View.c:1345
MutableArray * subviews
The immediate subviews.
Definition: View.h:252
descendantWithIdentifier(const View *self, const char *identifier)
Definition: View.c:491
void updateBindings(View *self)
Updates data bindings, prompting the appropriate layout changes.
View * superview
The super View.
Definition: View.h:258
SDL_Rect bounds(const View *self)
Definition: View.c:394
MutableArray * warnings
The warnings this View generated.
Definition: View.h:270
_Bool bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
Performs data binding for the Inlets described in dictionary.
int depth(const View *self)
Definition: View.c:483
void awakeWithData(View *self, const Data *data)
Wakes this View with the specified JSON Data.
Definition: View.c:315
void applyThemeIfNeeded(View *self, const Theme *theme)
Recursively applies the Theme to this View and its subviews.
Definition: View.c:269
ViewController * viewController
The ViewController.
Definition: View.h:264
SDL_Rect clippingFrame(const View *self)
Definition: View.c:429
void invalidateStyle(View *self)
Invalidates the computed Style for this View and its descendants.
Definition: View.c:822
void becomeFirstResponder(View *self)
Become the first responder in the View hierarchy.
Definition: View.c:370
_Bool clipsSubviews
If true, subviews will be clipped to this View's frame.
Definition: View.h:180
Style * style
The element-level Style of this View.
Definition: View.h:240
void awakeWithResource(View *self, const Resource *resource)
Wakes this View with the specified Resource.
Definition: View.c:346
ViewAlignment alignment
The alignment.
Definition: View.h:149
_Bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition: View.h:221
View * hitTest(const View *self, const SDL_Point *point)
Performs a hit test against this View and its descendants for the given point.
Definition: View.c:743
void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data)
Enumerates the superview of this View, if any, applying enumerator to it.
Definition: View.c:699
_Bool needsApplyTheme
If true, this View will apply the Theme before it is drawn.
Definition: View.h:216
SDL_Window * window
The window.
Definition: View.h:276
View * viewWithCharacters(const char *chars, Outlet *outlets)
Instantiates a View initialized with the given null-terminated JSON C string.
Definition: View.c:1558
_Bool hidden
If true, this View is not drawn.
Definition: View.h:195
Class * _View(void)
The View archetype.
Definition: View.c:1769
View * viewWithResource(const Resource *resource, Outlet *outlets)
Instantiates a View initialized with the JSON data in resource.
Definition: View.c:1603
Array * visibleSubviews(const View *self)
Definition: PageView.c:78
View * firstResponder(SDL_Window *window)
Definition: View.c:712
SDL_Color borderColor
The border color.
Definition: View.h:164
void addSubview(View *self, View *subview)
Adds a subview to this view, to be drawn above its siblings.
Definition: PageView.c:35
void addClassName(View *self, const char *className)
Adds the given class name to this View.
Definition: View.c:120
View * viewWithResourceName(const char *name, Outlet *outlets)
Instantiates a View initialized with the JSON Resource with the specified name.
Definition: View.c:1614
_Bool isFirstResponder(const View *self)
Definition: View.c:856
void applyStyle(View *self, const Style *style)
Applies the given Style to this View.
subviewWithIdentifier(const View *self, const char *identifier)
Definition: View.c:1507
void warn(View *self, const char *fmt,...)
Appends a warning for this View.
Definition: View.c:1647
void resize(View *self, const SDL_Size *size)
Resizes this View to the specified size.
Definition: View.c:1326
void awakeWithResourceName(View *self, const char *name)
Wakes this View with the Resource by the specified name.
Definition: View.c:357
void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all ancestors of this View, applying enumerator to each.
Definition: View.c:621
_Bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition: View.c:546
int autoresizingMask
The ViewAutoresizing bitmask.
Definition: View.h:154
void awakeWithCharacters(View *self, const char *chars)
Wakes this View with the given null-terminated JSON C string.
Definition: View.c:302
void setFirstResponder(SDL_Window *window, View *view)
Sets the first responder for the given window.
Definition: View.c:1393
void detachStylesheet(View *self, SDL_Window *window)
Detaches this View's Stylesheet from the Theme associated with the given window.
Definition: View.c:517
void removeFromSuperview(View *self)
Removes this View from its superview.
Definition: View.c:1175
View * nextResponder
The next responder, or event handler, in the chain.
Definition: View.h:228
SDL_Rect viewport(const View *self)
Definition: View.c:1545
void removeAllClassNames(View *self)
Removes all class names from this View.
Definition: View.c:1132
_Bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
char * identifier
An optional identifier.
Definition: View.h:201
void sizeThatFits(const View *self)
void * draw(View *self, Renderer *renderer)
Draws this View.
Definition: View.c:572
void respondToEvent(View *self, const SDL_Event *event)
Responds to the specified event.
Definition: Control.c:213
void removeAllSubviews(View *self)
Removes all subviews from this View.
Definition: View.c:1150
void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all siblings of this View, applying enumerator to each.
Definition: View.c:665
ViewPadding padding
The padding.
Definition: View.h:233
void bringSubviewToFront(View *self, View *subview)
Brings the specified subview to the front.
Definition: View.c:412
void replaceSubview(View *self, View *subview, View *replacement)
Replaces the specified subview with the given replacement.
Definition: View.c:1302
_Bool containsPoint(const View *self, const SDL_Point *point)
Definition: View.c:472
void awakeWithDictionary(View *self, const Dictionary *dictionary)
Wakes this View with the specified Dictionary.
Definition: Box.c:50
int borderWidth
The border width.
Definition: View.h:169
SDL_Size minSize
The minimum size this View may be resized to during layout.
Definition: View.h:211
void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all descendants of this View, applying enumerator to each.
Definition: View.c:647
void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data)
Enumerates adjacent siblings of this View, applying enumerator to each.
Definition: View.c:601
SDL_Rect renderFrame(const View *self)
Definition: View.c:1275
void resignFirstResponder(View *self)
Resigns first responder priority.
Definition: View.c:1315
_Bool isDescendantOfView(const View *self, const View *view)
Definition: View.c:838
void didMoveToWindow(View *self, SDL_Window *window)
Informs this View that it has been added to the View hierachy of the given window.
Definition: View.c:533
Style * computedStyle
The computed Style of this View.
Definition: View.h:185
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
Definition: Box.c:74
SDL_Size sizeThatContains(const View *self)
Definition: View.c:1423
void addSubviewRelativeTo(View *self, View *subview, View *other, ViewPosition position)
Adds a subview to this view, positioned relatively to other.
Definition: View.c:146
View * viewWithData(const Data *data, Outlet *outlets)
Instantiates a View initialized with the contents of data.
Definition: View.c:1573
void enumerate(const View *self, ViewEnumerator enumerator, ident data)
MutableSet * classNames
The class names.
Definition: View.h:175
SDL_Size maxSize
The maximum size this View may be resized to during layout.
Definition: View.h:206
_Bool isContainer(const View *self)
Definition: View.c:830
View * init(View *self)
Initializes this View.
Definition: Box.c:67
ancestorWithIdentifier(const View *self, const char *identifier)
Definition: View.c:188
void layoutIfNeeded(View *self)
Recursively updates the layout of this View and its subviews.
Definition: View.c:891
SDL_Color backgroundColor
The background color.
Definition: View.h:159
void applyTheme(View *self, const Theme *theme)
Applies the given Theme to this View.
Definition: View.c:246
void attachStylesheet(View *self, SDL_Window *window)
Attaches this View's Stylesheet to the Theme associated with the given window.
Definition: View.c:286
void moveToWindow(View *self, SDL_Window *window)
Moves this View to the View hierarchy of the given window.
Definition: View.c:1073
SDL_Rect frame
The frame, relative to the superview.
Definition: View.h:190
void sizeToContain(View *self)
Resizes this View to contain its subviews.
Definition: View.c:1485
void willMoveToWindow(View *self, SDL_Window *window)
Informs this View that it will be added to the View hierarchy for the given window.
Definition: View.c:1664
_Bool acceptsFirstResponder(const View *self)
Definition: Control.c:68
void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all subviews of this View, applying enumerator to each.
Definition: View.c:685
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
Definition: Control.c:131
void sizeToFit(View *self)
Resizes this View to fit its subviews.
Definition: View.c:1496
void removeClassName(View *self, const char *className)
Removes the given class name to this View.
Definition: View.c:1158
void removeSubview(View *self, View *subview)
Removes the given subview from this View.
Definition: PageView.c:58
View * viewWithDictionary(const Dictionary *dictionary, Outlet *outlets)
Instantiates a View initialized with the attributes described in dictionary.
Definition: View.c:1588
int top
Definition: View.h:100
int bottom
Definition: View.h:100
int right
Definition: View.h:100
int left
Definition: View.h:100