ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
View.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 "Log.h"
31#include "View.h"
32#include "ViewController.h"
33#include "WindowController.h"
34#include "Window.h"
35
38
44static unsigned _renderFrameGeneration;
45
47 if (++_renderFrameGeneration == 0) {
49 }
50}
51
52const EnumName ViewPointerEventsNames[] = MakeEnumNames(
53 MakeEnumAlias(ViewPointerEventsAuto, auto),
54 MakeEnumAlias(ViewPointerEventsNone, none)
55);
56
57const EnumName ViewVisibilityNames[] = MakeEnumNames(
58 MakeEnumAlias(ViewVisibilityUnspecified, unspecified),
59 MakeEnumAlias(ViewVisibilityVisible, visible),
60 MakeEnumAlias(ViewVisibilityHidden, hidden)
61);
62
63const EnumName ViewAlignmentNames[] = MakeEnumNames(
64 MakeEnumAlias(ViewAlignmentNone, none),
65 MakeEnumAlias(ViewAlignmentTop, top),
66 MakeEnumAlias(ViewAlignmentMiddle, middle),
67 MakeEnumAlias(ViewAlignmentBottom, bottom),
68 MakeEnumAlias(ViewAlignmentLeft, left),
69 MakeEnumAlias(ViewAlignmentCenter, center),
70 MakeEnumAlias(ViewAlignmentRight, right),
71 MakeEnumAlias(ViewAlignmentTopLeft, top-left),
72 MakeEnumAlias(ViewAlignmentTopCenter, top-center),
73 MakeEnumAlias(ViewAlignmentTopRight, top-right),
74 MakeEnumAlias(ViewAlignmentMiddleLeft, middle-left),
75 MakeEnumAlias(ViewAlignmentMiddleCenter, middle-center),
76 MakeEnumAlias(ViewAlignmentMiddleRight, middle-right),
77 MakeEnumAlias(ViewAlignmentBottomLeft, bottom-left),
78 MakeEnumAlias(ViewAlignmentBottomCenter, bottom-center),
79 MakeEnumAlias(ViewAlignmentBottomRight, bottom-right),
80 MakeEnumAlias(ViewAlignmentInternal, internal)
81);
82
83const EnumName ViewAutoresizingNames[] = MakeEnumNames(
84 MakeEnumAlias(ViewAutoresizingNone, none),
85 MakeEnumAlias(ViewAutoresizingWidth, width),
86 MakeEnumAlias(ViewAutoresizingHeight, height),
87 MakeEnumAlias(ViewAutoresizingFill, fill),
88 MakeEnumAlias(ViewAutoresizingFit, fit),
89 MakeEnumAlias(ViewAutoresizingContain, contain)
90);
91
92#define _Class _View
93
94#pragma mark - Object
95
99static bool filterViewEvents(void *data, SDL_Event *event) {
100
101 if (event->type == MVC_VIEW_EVENT && event->user.data1 == data) {
102 return false;
103 }
104
105 return true;
106}
107
111static void nullifySuperview(View *subview, ident data) {
112 subview->superview = NULL;
113}
114
118static void dealloc(Object *self) {
119
120 View *this = (View *) self;
121
122 SDL_FilterEvents(filterViewEvents, this);
123
124 $(this, moveToWindow, NULL);
125
126 $(this, enumerateSubviews, nullifySuperview, NULL);
127
128 free(this->identifier);
129
130 release(this->classNames);
131 release(this->computedStyle);
132 release(this->style);
133 release(this->stylesheet);
134 release(this->subviews);
135 release(this->warnings);
136
137 this->superview = NULL;
138
139 super(Object, self, dealloc);
140}
141
145static String *description(const Object *self) {
146
147 View *this = (View *) self;
148 const SDL_Rect bounds = $(this, bounds);
149
150 String *classNames = $((Object *) this->classNames, description);
151 String *description = str("%s@%p %s [%d, %d, %d, %d]",
152 this->identifier ?: classnameof(self),
153 self,
154 classNames->chars,
155 bounds.x, bounds.y, bounds.w, bounds.h);
156
157 release(classNames);
158 return description;
159}
160
161#pragma mark - View
162
167static bool acceptsKeyResponder(const View *self) {
168 return false;
169}
170
175static bool acceptsTouchResponder(const View *self) {
176 return false;
177}
178
183static void addClassName(View *self, const char *className) {
184
185 if (className) {
186
187 String *string = $$(String, stringWithCharacters, className);
188 assert(string);
189
190 $(self->classNames, addObject, string);
191 release(string);
192
193 $(self, invalidateStyle);
194 }
195}
196
201static void addSubview(View *self, View *subview) {
202 $(self, addSubviewRelativeTo, subview, NULL, ViewPositionAfter);
203}
204
209static void addSubviewRelativeTo(View *self, View *subview, View *other, ViewPosition position) {
210
211 assert(subview);
212 assert(subview != other);
213
214 retain(subview);
215
216 $(subview, removeFromSuperview);
217
218 if (other && other->superview == self) {
219
220 const Array *subviews = (Array *) self->subviews;
221 const ssize_t index = $(subviews, indexOfObject, other);
222
223 if (position == ViewPositionAfter) {
224 if (index == (ssize_t) (subviews->count - 1)) {
225 $(self->subviews, addObject, subview);
226 } else {
227 $(self->subviews, insertObjectAtIndex, subview, index + 1);
228 }
229 } else {
230 $(self->subviews, insertObjectAtIndex, subview, index);
231 }
232 } else {
233 $(self->subviews, addObject, subview);
234 }
235
236 release(subview);
237
238 subview->superview = self;
239
240 $(subview, moveToWindow, self->window);
241
242 $(subview, invalidateStyle);
243
244 $(self, setNeedsLayout);
245}
246
251static View *ancestorWithIdentifier(const View *self, const char *identifier) {
252
253 assert(identifier);
254
255 View *view = (View *) self;
256 while (view) {
257 if (view->identifier) {
258 if (strcmp(identifier, view->identifier) == 0) {
259 return view;
260 }
261 }
262 view = view->superview;
263 }
264
265 return NULL;
266}
267
272static void applyStyle(View *self, const Style *style) {
273
274 assert(style);
275
276 const Inlet inlets[] = MakeInlets(
277 MakeInlet("alignment", InletTypeEnum, &self->alignment, (ident) ViewAlignmentNames),
278 MakeInlet("autoresizing-mask", InletTypeEnum, &self->autoresizingMask, (ident) ViewAutoresizingNames),
279 MakeInlet("background-color", InletTypeColor, &self->backgroundColor, NULL),
280 MakeInlet("background-gradient-angle", InletTypeInteger, &self->backgroundGradientAngle, NULL),
281 MakeInlet("background-gradient-color", InletTypeColor, &self->backgroundGradientColor, NULL),
282 MakeInlet("border-color", InletTypeColor, &self->borderColor, NULL),
283 MakeInlet("border-radius", InletTypeInteger, &self->borderRadius, NULL),
284 MakeInlet("border-width", InletTypeInteger, &self->borderWidth, NULL),
285 MakeInlet("clips-subviews", InletTypeBool, &self->clipsSubviews, NULL),
286 MakeInlet("corner-cut", InletTypeRectangle, &self->cornerCut, NULL),
287 MakeInlet("corner-cut-top-left", InletTypeInteger, &self->cornerCut.topLeft, NULL),
288 MakeInlet("corner-cut-top-right", InletTypeInteger, &self->cornerCut.topRight, NULL),
289 MakeInlet("corner-cut-bottom-right", InletTypeInteger, &self->cornerCut.bottomRight, NULL),
290 MakeInlet("corner-cut-bottom-left", InletTypeInteger, &self->cornerCut.bottomLeft, NULL),
291 MakeInlet("height", InletTypeInteger, &self->frame.h, NULL),
292 MakeInlet("left", InletTypeInteger, &self->frame.x, NULL),
293 MakeInlet("max-height", InletTypeInteger, &self->maxSize.h, NULL),
294 MakeInlet("max-size", InletTypeSize, &self->maxSize, NULL),
295 MakeInlet("max-width", InletTypeInteger, &self->maxSize.w, NULL),
296 MakeInlet("min-height", InletTypeInteger, &self->minSize.h, NULL),
297 MakeInlet("min-size", InletTypeSize, &self->minSize, NULL),
298 MakeInlet("min-width", InletTypeInteger, &self->minSize.w, NULL),
299 MakeInlet("padding", InletTypeRectangle, &self->padding, NULL),
300 MakeInlet("padding-top", InletTypeInteger, &self->padding.top, NULL),
301 MakeInlet("padding-right", InletTypeInteger, &self->padding.right, NULL),
302 MakeInlet("padding-bottom", InletTypeInteger, &self->padding.bottom, NULL),
303 MakeInlet("padding-left", InletTypeInteger, &self->padding.left, NULL),
304 MakeInlet("pointer-events", InletTypeEnum, &self->pointerEvents, (ident) ViewPointerEventsNames),
305 MakeInlet("stretch", InletTypeBool, &self->stretch, NULL),
306 MakeInlet("top", InletTypeInteger, &self->frame.y, NULL),
307 MakeInlet("visibility", InletTypeEnum, &self->visibility, (ident) ViewVisibilityNames),
308 MakeInlet("width", InletTypeInteger, &self->frame.w, NULL)
309 );
310
311 const ViewVisibility visibility = self->visibility;
312 const bool stretch = self->stretch;
313
314 $(self, bind, inlets, style->attributes);
315
316 if ((self->visibility != visibility || self->stretch != stretch) && self->superview) {
317 $(self->superview, setNeedsLayout);
318 }
319
321 $(style->attributes, objectForKeyPath, "left")) {
322 $(self, warn, WarningTypeStyle, "`left` is overruled by the horizontal `alignment`");
323 }
324
325 if ((self->alignment & ViewAlignmentMaskVertical) &&
326 $(style->attributes, objectForKeyPath, "top")) {
327 $(self, warn, WarningTypeStyle, "`top` is overruled by the vertical `alignment`");
328 }
329}
330
335static void applyTheme(View *self, const Theme *theme) {
336
337 assert(theme);
338
339 Style *computedStyle = $(theme, computeStyle, self);
340 assert(computedStyle);
341
342 if (self->needsApplyStyle || !$(self->computedStyle, isComputedEqual, computedStyle)) {
343
344 release(self->computedStyle);
345 self->computedStyle = retain(computedStyle);
346
348 $(self, applyStyle, self->computedStyle);
349
350 self->needsApplyStyle = false;
351 }
352
353 release(computedStyle);
354}
355
360static void applyThemeIfNeeded(View *self, const Theme *theme);
361
365static void _applyThemeIfNeeded(View *view, ident data) {
366 applyThemeIfNeeded(view, (const Theme *) data);
367}
368
369static void applyThemeIfNeeded(View *self, const Theme *theme) {
370
371 assert(theme);
372
373 if (!self->needsApplyTheme && !self->needsApplyThemeSubviews) {
374 return;
375 }
376
377 // Cleared before descending so that any re-invalidation during the traversal
378 // propagates fresh subtree flags and survives to the next frame.
379 self->needsApplyThemeSubviews = false;
380
382
383 if (self->needsApplyTheme) {
384
386
387 $(self, applyTheme, theme);
388
389 self->needsApplyTheme = false;
390 }
391}
392
397static void attachStylesheet(View *self, SDL_Window *window) {
398
399 assert(window);
400
401 if (self->stylesheet) {
402 Theme *theme = $$(Theme, theme, window);
403 if (theme) {
404 $(theme, addStylesheet, self->stylesheet);
405 }
406 }
407}
408
413static void awakeWithCharacters(View *self, const char *chars) {
414
415 Data *data = $$(Data, dataWithConstMemory, (uint8_t *) chars, strlen(chars));
416
417 $(self, awakeWithData, data);
418
419 release(data);
420}
421
426static void awakeWithData(View *self, const Data *data) {
427
428 JSONContext *ctx = $(alloc(JSONContext), init);
429 Dictionary *dictionary = $(ctx, objectFromData, data, 0);
430
431 if (dictionary) {
432 $(self, awakeWithDictionary, dictionary);
433 release(dictionary);
434 } else {
435 MVC_LogError("Failed to parse JSON for %s\n", classnameof(self));
436 }
437
438 release(ctx);
439}
440
445static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
446
447 assert(dictionary);
448
449 const Inlet inlets[] = MakeInlets(
450 MakeInlet("identifier", InletTypeCharacters, &self->identifier, NULL),
451 MakeInlet("classNames", InletTypeClassNames, &self, NULL),
452 MakeInlet("style", InletTypeStyle, &self, NULL),
453 MakeInlet("subviews", InletTypeSubviews, &self, NULL)
454 );
455
456 $(self, bind, inlets, dictionary);
457}
458
463static void awakeWithResource(View *self, const Resource *resource) {
464
465 assert(resource);
466
467 $(self, awakeWithData, resource->data);
468}
469
474static void awakeWithResourceName(View *self, const char *name) {
475
476 Resource *resource = $$(Resource, resourceWithName, name);
477
478 $(self, awakeWithResource, resource);
479
480 release(resource);
481}
482
487static void becomeKeyResponder(View *self) {
488
489 assert(self->window);
490
491 if (!$(self, isKeyResponder)) {
492 SDL_PropertiesID props = SDL_GetWindowProperties(self->window);
493
494 View *keyResponder = SDL_GetPointerProperty(props, "keyResponder", NULL);
495 if (keyResponder) {
497 }
498
499 String *path = $(self, path);
500 MVC_LogDebug("%s\n", path->chars);
501 release(path);
502
503 SDL_SetPointerProperty(props, "keyResponder", self);
504 }
505}
506
511static void becomeTouchResponder(View *self) {
512
513 assert(self->window);
514
515 if (!$(self, isTouchResponder)) {
516 SDL_PropertiesID props = SDL_GetWindowProperties(self->window);
517
518 View *touchResponder = SDL_GetPointerProperty(props, "touchResponder", NULL);
519 if (touchResponder) {
521 }
522
523 String *path = $(self, path);
524 MVC_LogDebug("%s\n", path->chars);
525 release(path);
526
527 SDL_SetPointerProperty(props, "touchResponder", self);
528 }
529}
530
535static bool _bind(View *self, const Inlet *inlets, const Dictionary *dictionary) {
536
537 if (inlets) {
538 if (bindInlets(inlets, dictionary)) {
539 $(self, setNeedsApplyTheme);
540 $(self, setNeedsLayout);
541 return true;
542 }
543 }
544
545 return false;
546}
547
552static SDL_Rect bounds(const View *self) {
553
554 const SDL_Size size = $(self, size);
555
556 const SDL_Rect bounds = {
557 .x = self->padding.left,
558 .y = self->padding.top,
559 .w = max(0, size.w - (self->padding.left + self->padding.right)),
560 .h = max(0, size.h - (self->padding.top + self->padding.bottom)),
561 };
562
563 return bounds;
564}
565
570static void bringSubviewToFront(View *self, View *subview) {
571
572 assert(subview);
573
574 if (subview->superview == self) {
575
576 View *last = $((Array *) self->subviews, lastObject);
577 if (last != subview) {
578 $(self, addSubviewRelativeTo, subview, last, ViewPositionAfter);
579 }
580 }
581}
582
586static bool clearWarnings_predicate(const ident obj, ident data) {
587
588 const Warning *warning = obj;
589 const WarningType type = *(WarningType *) data;
590
591 return (warning->type & type) == 0;
592}
593
598static void clearWarnings(const View *self, WarningType type) {
599 $(self->warnings, filter, clearWarnings_predicate, &type);
600}
601
606static SDL_Rect clippingFrame(const View *self) {
607
608 View *this = (View *) self;
609
610 if (_renderFrameGeneration && this->clippingFrameCache.generation == _renderFrameGeneration) {
611 return this->clippingFrameCache.frame;
612 }
613
614 SDL_Rect frame = $(self, renderFrame);
615
616 if (self->borderWidth && self->borderColor.a) {
617 frame.x -= self->borderWidth;
618 frame.y -= self->borderWidth;
619 frame.w += self->borderWidth * 2;
620 frame.h += self->borderWidth * 2;
621 }
622
623 // The nearest clipping ancestor's clippingFrame already folds in every outer clip, so a
624 // single intersection with it is equivalent to intersecting each clipping ancestor in turn.
625 const View *superview = self->superview;
626 while (superview && !superview->clipsSubviews) {
627 superview = superview->superview;
628 }
629
630 if (superview) {
631 const SDL_Rect clippingFrame = $(superview, clippingFrame);
632 if (SDL_GetRectIntersection(&clippingFrame, &frame, &frame) == false) {
633 frame.w = frame.h = 0;
634 }
635 }
636
637 this->clippingFrameCache.frame = frame;
638 this->clippingFrameCache.generation = _renderFrameGeneration;
639
640 return frame;
641}
642
647static bool containsPoint(const View *self, const SDL_Point *point) {
648
649 const SDL_Rect frame = $(self, clippingFrame);
650
651 return (bool) SDL_PointInRect(point, &frame);
652}
653
658static int depth(const View *self) {
659 return (self->superview ? $(self->superview, depth) + 1 : 0);
660}
661
666static View *descendantWithIdentifier(const View *self, const char *identifier) {
667
668 assert(identifier);
669
670 if (self->identifier) {
671 if (strcmp(identifier, self->identifier) == 0) {
672 return (View *) self;
673 }
674 }
675
676 const Array *subviews = (Array *) self->subviews;
677 for (size_t i = 0; i < subviews->count; i++) {
678
679 const View *subview = subviews->elements[i];
680 View *descendant = $(subview, descendantWithIdentifier, identifier);
681 if (descendant) {
682 return descendant;
683 }
684 }
685
686 return NULL;
687}
688
693static void detachStylesheet(View *self, SDL_Window *window) {
694
695 assert(window);
696
697 if (self->stylesheet) {
698 Theme *theme = $$(Theme, theme, window);
699 if (theme) {
701 }
702 }
703}
704
709static void didMoveToWindow(View *self, SDL_Window *window) {
710
711 if (window) {
712 $(self, attachStylesheet, window);
713
714 if (self->superview == NULL) {
715 $(self, sizeToFill);
716 }
717
718 $(self, setNeedsLayout);
719 }
720}
721
726static bool didReceiveEvent(const View *self, const SDL_Event *event) {
727
728 if ($(self, isKeyResponder)) {
729 switch (event->type) {
730 case SDL_EVENT_KEY_DOWN:
731 case SDL_EVENT_KEY_UP:
732 case SDL_EVENT_TEXT_INPUT:
733 return true;
734 }
735 }
736
737 if ($(self, isVisible)) {
738
739 SDL_Point point;
740
741 switch (event->type) {
742 case SDL_EVENT_MOUSE_BUTTON_DOWN:
743 case SDL_EVENT_MOUSE_BUTTON_UP:
744 point = MakePoint(event->button.x, event->button.y);
745 break;
746 case SDL_EVENT_MOUSE_MOTION:
747 point = MakePoint(event->motion.x, event->motion.y);
748 break;
749 case SDL_EVENT_MOUSE_WHEEL: {
750 float mx, my;
751 SDL_GetMouseState(&mx, &my);
752 point = MakePoint(mx, my);
753 break;
754 }
755 default:
756 return false;
757 }
758
759 return $(self, containsPoint, &point);
760 }
761
762 return false;
763}
764
769static void draw(View *self, Renderer *renderer);
770
774static void _draw(View *view, ident data) {
775 draw(view, (Renderer *) data);
776}
777
778static void draw(View *self, Renderer *renderer) {
779
780 assert(self->window);
781
782 if (self->visibility != ViewVisibilityHidden) {
783
784 $(renderer, drawView, self);
785
786 $(self, enumerateSubviews, _draw, renderer);
787 }
788}
789
794static void emitViewEvent(View *self, ViewEvent code, ident data) {
795 SDL_PushEvent((SDL_Event *) &(const SDL_UserEvent) {
796 .type = MVC_VIEW_EVENT,
797 .code = code,
798 .data1 = self,
799 .data2 = data
800 });
801}
802
807static void enumerate(View *self, ViewEnumerator enumerator, ident data) {
808
809 assert(enumerator);
810
811 enumerator(self, data);
812
813 $(self, enumerateDescendants, enumerator, data);
814}
815
820static void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data) {
821
822 assert(enumerator);
823
824 if (self->superview) {
825 const Array *siblings = (Array *) self->superview->subviews;
826 const ssize_t index = $(siblings, indexOfObject, (const ident) self);
827 if (index > 0) {
828 enumerator($(siblings, objectAtIndex, index - 1), data);
829 }
830 if (index < (ssize_t) (siblings->count - 1)) {
831 enumerator($(siblings, objectAtIndex, index + 1), data);
832 }
833 }
834}
835
840static void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data) {
841
842 assert(enumerator);
843
844 for (View *view = self->superview; view; view = view->superview) {
845 enumerator(view, data);
846 }
847}
848
853static void enumerateSelection(View *self, const char *rule, ViewEnumerator enumerator, ident data) {
854
855 Selector *selector = $(alloc(Selector), initWithRule, rule);
856 assert(selector);
857
858 $(selector, enumerateSelection, self, enumerator, data);
859 release(selector);
860}
861
866static void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data) {
867
868 assert(enumerator);
869
870 const Array *subviews = (Array *) self->subviews;
871 for (size_t i = 0; i < subviews->count; i++) {
872
873 View *subview = subviews->elements[i];
874 enumerator(subview, data);
875
876 $(subview, enumerateDescendants, enumerator, data);
877 }
878}
879
884static void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data) {
885
886 assert(enumerator);
887
888 if (self->superview) {
889
890 const Array *siblings = (Array *) self->superview->subviews;
891 for (size_t i = 0; i < siblings->count; i++) {
892 View *sibling = siblings->elements[i];
893 if (sibling != self) {
894 enumerator(sibling, data);
895 }
896 }
897 }
898}
899
904static void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data) {
905
906 assert(enumerator);
907
908 const Array *subviews = (Array *) self->subviews;
909 for (size_t i = 0; i < subviews->count; i++) {
910 enumerator((View *) subviews->elements[i], data);
911 }
912}
913
918static void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data) {
919
920 assert(enumerator);
921
922 if (self->superview) {
923 enumerator(self->superview, data);
924 }
925}
926
931static void enumerateVisible(View *self, ViewEnumerator enumerator, ident data) {
932
933 if ($(self, isVisible) == false) {
934 return;
935 }
936
937 enumerator(self, data);
938
939 const Array *subviews = (Array *) self->subviews;
940 for (size_t i = 0; i < subviews->count; i++) {
941
942 View *subview = subviews->elements[i];
943 $(subview, enumerateVisible, enumerator, data);
944 }
945}
946
950static bool hasClassName_predicate(const ident obj, ident data) {
951 return strcmp(((String *) obj)->chars, (const char *) data) == 0;
952}
953
958static bool hasClassName(const View *self, const char *className) {
959
960 if (className) {
961 return $((Set *) self->classNames, containsObjectMatching, hasClassName_predicate, (ident) className);
962 }
963
964 return false;
965}
966
967typedef struct {
968 SDL_Rect bounds;
970} Overflow;
971
975static void hasOverflow_enumerate(View *view, ident data) {
976
977 Overflow *overflow = data;
978
979 if (view->visibility != ViewVisibilityHidden) {
980
981 const SDL_Rect bounds = $(view, bounds);
982
983 if (bounds.x + bounds.w > overflow->bounds.w || bounds.y + bounds.h > overflow->bounds.h) {
984
985 overflow->hasOverflow = true;
986
987 $(view, warn, WarningTypeLayout, "Exceeds superview bounds [%d %d %d %d]",
988 overflow->bounds.x, overflow->bounds.y, overflow->bounds.w, overflow->bounds.h);
989 }
990 }
991}
992
997static bool hasOverflow(const View *self) {
998
999 Overflow overflow = {
1000 .bounds = $(self, bounds)
1001 };
1002
1003 $(self, enumerateSubviews, hasOverflow_enumerate, (ident) &overflow);
1004
1005 return overflow.hasOverflow;
1006}
1007
1012static View *hitTest(const View *self, const SDL_Point *point) {
1013
1014 if (self->visibility != ViewVisibilityHidden) {
1015
1016 if ($(self, containsPoint, point)) {
1017
1018 const Array *subviews = (Array *) self->subviews;
1019 for (size_t i = subviews->count; i; i--) {
1020
1021 const View *subview = subviews->elements[i - 1];
1022 const View *view = $(subview, hitTest, point);
1023 if (view) {
1024 return (View *) view;
1025 }
1026 }
1027
1028 return self->pointerEvents == ViewPointerEventsAuto ? (View *) self : NULL;
1029 }
1030 }
1031
1032 return NULL;
1033}
1034
1039static View *init(View *self) {
1040 return $(self, initWithFrame, NULL);
1041}
1042
1047static View *initWithFrame(View *self, const SDL_Rect *frame) {
1048
1049 self = (View *) super(Object, self, init);
1050 if (self) {
1051
1052 if (frame) {
1053 self->frame = *frame;
1054 }
1055
1056 self->classNames = $$(Set, setWithCapacity, 0);
1057 assert(self->classNames);
1058
1059 self->computedStyle = $(alloc(Style), initWithAttributes, NULL);
1060 assert(self->computedStyle);
1061
1062 self->subviews = $$(Array, arrayWithCapacity, 0);
1063 assert(self->subviews);
1064
1065 self->style = $(alloc(Style), initWithAttributes, NULL);
1066 assert(self->style);
1067
1068 self->warnings = $$(Array, arrayWithCapacity, 0);
1069 assert(self->warnings);
1070
1071 self->needsApplyTheme = true;
1072 self->needsLayout = true;
1073 }
1074
1075 return self;
1076}
1077
1081static void invalidateStyle_enumerate(View *view, ident data) {
1082
1083 view->needsApplyStyle = true;
1084
1085 $(view, setNeedsApplyTheme);
1086}
1087
1092static void invalidateStyle(View *self) {
1093 $(self, enumerate, invalidateStyle_enumerate, NULL);
1094}
1095
1100static bool isContainer(const View *self) {
1102}
1103
1108static bool isDescendantOfView(const View *self, const View *view) {
1109
1110 assert(view);
1111
1112 while (self) {
1113 if (self == view) {
1114 return true;
1115 }
1116 self = self->superview;
1117 }
1118
1119 return false;
1120}
1121
1126static bool isKeyResponder(const View *self) {
1127
1128 if (self->window) {
1129 return SDL_GetPointerProperty(SDL_GetWindowProperties(self->window), "keyResponder", NULL) == self;
1130 } else {
1131 return false;
1132 }
1133}
1134
1139static bool isTouchResponder(const View *self) {
1140
1141 if (self->window) {
1142 return SDL_GetPointerProperty(SDL_GetWindowProperties(self->window), "touchResponder", NULL) == self;
1143 } else {
1144 return false;
1145 }
1146}
1147
1152static bool isVisible(const View *self) {
1153
1154 for (const View *view = self; view; view = view->superview) {
1155 if (view->visibility == ViewVisibilityHidden) {
1156 return false;
1157 }
1158 }
1159
1160 return true;
1161}
1162
1166static void layoutIfNeeded_enumerate(View *subview, ident data) {
1167 $(subview, layoutIfNeeded);
1168}
1169
1174static void layoutIfNeeded(View *self) {
1175
1176 if (!self->needsLayout && !self->needsLayoutSubviews) {
1177 return;
1178 }
1179
1180 // Cleared before laying out so that any re-invalidation during layout (e.g. a resize
1181 // propagating setNeedsLayout, or a widget marking a sibling) survives to the next frame.
1182 self->needsLayoutSubviews = false;
1183
1184 // Subviews first, so that a dirty descendant whose layout resizes it marks its ancestors
1185 // before their own dirty checks below run; the whole tree then converges bottom-up in a
1186 // single pass, exactly as invalidations propagate.
1188
1189 if (self->needsLayout) {
1190
1191 // No ancestor is actively arranging self right now, so there's no fresh constraint to
1192 // consult; treat an existing frame as authoritative per-axis (Equal, not Max -- see
1193 // ViewConstraintMax), leaving an axis with no frame yet Unspecified.
1194 const ViewConstraint w = self->frame.w ?
1195 MakeConstraint(ViewConstraintEqual, self->frame.w) : MakeConstraint(ViewConstraintUnspecified, 0);
1196 const ViewConstraint h = self->frame.h ?
1197 MakeConstraint(ViewConstraintEqual, self->frame.h) : MakeConstraint(ViewConstraintUnspecified, 0);
1198
1199 $(self, layoutWithConstraint, w, h);
1200 }
1201}
1202
1207static void layoutSubviews(View *self) {
1208
1209 const SDL_Rect bounds = $(self, bounds);
1210
1211 const Array *subviews = (Array *) self->subviews;
1212 for (size_t i = 0; i < subviews->count; i++) {
1213
1214 View *subview = subviews->elements[i];
1215
1217 MakeConstraint(ViewConstraintEqual, bounds.w) : MakeConstraint(ViewConstraintUnspecified, 0);
1218
1220 MakeConstraint(ViewConstraintEqual, bounds.h) : MakeConstraint(ViewConstraintUnspecified, 0);
1221
1222 $(subview, layoutWithConstraint, w, h);
1223
1224 switch (subview->alignment & ViewAlignmentMaskHorizontal) {
1225 case ViewAlignmentLeft:
1226 subview->frame.x = 0;
1227 break;
1229 subview->frame.x = (bounds.w - subview->frame.w) * 0.5f;
1230 break;
1231 case ViewAlignmentRight:
1232 subview->frame.x = bounds.w - subview->frame.w;
1233 break;
1234 }
1235
1236 switch (subview->alignment & ViewAlignmentMaskVertical) {
1238 subview->frame.y = 0;
1239 break;
1241 subview->frame.y = (bounds.h - subview->frame.h) * 0.5f;
1242 break;
1244 subview->frame.y = bounds.h - subview->frame.h;
1245 break;
1246 }
1247 }
1248}
1249
1254static void layoutWithConstraint(View *self, ViewConstraint width, ViewConstraint height) {
1255
1256 $(self, sizeToSatisfy, width, height);
1258 $(self, layoutSubviews);
1259
1260 self->needsLayout = false;
1261}
1262
1267static void layoutWithSize(View *self, const SDL_Size *size) {
1268
1269 $(self, resize, size);
1271 $(self, layoutSubviews);
1272
1273 self->needsLayout = false;
1274}
1275
1280static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector) {
1281
1282 assert(simpleSelector);
1283
1284 const char *pattern = simpleSelector->pattern;
1285
1286 switch (simpleSelector->type) {
1288 return false;
1289
1291 return true;
1292
1294 const Class *clazz = classForName(pattern);
1295 if (clazz) {
1296 return $((Object *) self, isKindOfClass, clazz);
1297 }
1298 }
1299 break;
1300
1302 return $(self, hasClassName, pattern);
1303
1305 if (self->identifier) {
1306 return strcmp(self->identifier, pattern) == 0;
1307 } else {
1308 return false;
1309 }
1310
1312 if (strcmp("first-child", pattern) == 0) {
1313 if (self->superview) {
1314 return $((Array *) self->superview->subviews, firstObject) == self;
1315 }
1316 } else if (strcmp("last-child", pattern) == 0) {
1317 if (self->superview) {
1318 return $((Array *) self->superview->subviews, lastObject) == self;
1319 }
1320 } else if (strcmp("nth-child(even)", pattern) == 0) {
1321 if (self->superview) {
1322 return ($((Array *) self->superview->subviews, indexOfObject, (ident) self) & 1) == 0;
1323 }
1324 } else if (strcmp("nth-child(odd)", pattern) == 0) {
1325 if (self->superview) {
1326 return $((Array *) self->superview->subviews, indexOfObject, (ident) self) & 1;
1327 }
1328 } else if (strcmp("hover", pattern) == 0) {
1329 float mx, my;
1330 SDL_GetMouseState(&mx, &my);
1331 SDL_Point point = MakePoint(mx, my);
1332 return $(self, containsPoint, &point);
1333 }
1334 break;
1335 }
1336
1337 return false;
1338}
1339
1343static void moveToWindow_enumerate(View *subview, ident data) {
1344 $(subview, moveToWindow, data);
1345}
1346
1351static void moveToWindow(View *self, SDL_Window *window) {
1352
1353 $(self, willMoveToWindow, window);
1354
1355 if (self->window && self->window != window) {
1356 SDL_PropertiesID props = SDL_GetWindowProperties(self->window);
1357
1358 if (SDL_GetPointerProperty(props, "keyResponder", NULL) == self) {
1359 SDL_SetPointerProperty(props, "keyResponder", NULL);
1360 }
1361
1362 if (SDL_GetPointerProperty(props, "touchResponder", NULL) == self) {
1363 SDL_SetPointerProperty(props, "touchResponder", NULL);
1364 }
1365 }
1366
1367 self->window = window;
1368
1369 $(self, didMoveToWindow, window);
1370
1371 $(self, enumerateSubviews, moveToWindow_enumerate, window);
1372}
1373
1378static String *path(const View *self) {
1379
1380 Array *parts = $$(Array, array);
1381
1382 const View *view = self;
1383 while (view) {
1384
1385 Array *classNames = $((Set *) view->classNames, allObjects);
1386
1387 String *part;
1388 if (view->identifier) {
1389 part = str("#%s", view->identifier);
1390 } else if (classNames->count) {
1391 part = str(".%s", ((String *) $(classNames, firstObject))->chars);
1392 } else {
1393 part = str("%s", classnameof(view));
1394 }
1395
1396 release(classNames);
1397
1398 $(parts, insertObjectAtIndex, part, 0);
1399 release(part);
1400
1401 if (view->viewController) {
1402 break;
1403 }
1404
1405 view = view->superview;
1406 }
1407
1408 String *path = $((Array *) parts, componentsJoinedByCharacters, " > ");
1409
1410 release(parts);
1411
1412 return path;
1413}
1414
1419static void removeAllClassNames(View *self) {
1420
1421 $(self->classNames, removeAllObjects);
1422
1423 $(self, invalidateStyle);
1424}
1425
1429static void removeAllSubviews_enumerate(const Array *array, ident obj, ident data) {
1430 $((View *) data, removeSubview, obj);
1431}
1432
1437static void removeAllSubviews(View *self) {
1438 $(self->subviews, removeAllObjectsWithEnumerator, removeAllSubviews_enumerate, self);
1439}
1440
1445static void removeClassName(View *self, const char *className) {
1446
1447 if (className) {
1448 String *string = $$(String, stringWithCharacters, className);
1449 assert(string);
1450
1451 $(self->classNames, removeObject, string);
1452 release(string);
1453
1454 $(self, invalidateStyle);
1455 }
1456}
1457
1462static void removeFromSuperview(View *self) {
1463
1464 if (self->superview) {
1465 $(self->superview, removeSubview, self);
1466 }
1467}
1468
1473static void removeSubview(View *self, View *subview) {
1474
1475 assert(subview);
1476
1477 if (subview->superview == self) {
1478
1479 subview->superview = NULL;
1480
1481 $(subview, moveToWindow, NULL);
1482
1483 $(self->subviews, removeObject, subview);
1484
1485 $(self, setNeedsLayout);
1486 }
1487}
1488
1496static void cornerCutPoints(SDL_Point *points, const SDL_Rect *rect, const ViewCornerCut *cut) {
1497
1498 const int x1 = rect->x, y1 = rect->y;
1499 const int x2 = rect->x + rect->w, y2 = rect->y + rect->h;
1500
1501 const int w = rect->w;
1502
1503 const int topLeft = clamp(cut->topLeft, 0, w);
1504 const int topRight = clamp(cut->topRight, 0, w - topLeft);
1505
1506 const int bottomLeft = clamp(cut->bottomLeft, 0, w);
1507 const int bottomRight = clamp(cut->bottomRight, 0, w - bottomLeft);
1508
1509 points[0] = MakePoint(x1 + topLeft, y1);
1510 points[1] = MakePoint(x2 - topRight, y1);
1511 points[2] = MakePoint(x2 - bottomRight, y2);
1512 points[3] = MakePoint(x1 + bottomLeft, y2);
1513}
1514
1519static void render(View *self, Renderer *renderer) {
1520
1521 assert(self->window);
1522
1523 if ($(self, hasClassName, "breakpoint")) {
1524 SDL_TriggerBreakpoint();
1525 }
1526
1527 const bool cut = !CornerCutIsEmpty(self->cornerCut);
1528
1529 if (self->backgroundColor.a || self->backgroundGradientColor.a) {
1530
1531 const SDL_Rect frame = $(self, renderFrame);
1532 const SDL_Color *gradient = self->backgroundGradientColor.a ? &self->backgroundGradientColor : NULL;
1533
1534 if (cut) {
1535 SDL_Point points[4];
1536 cornerCutPoints(points, &frame, &self->cornerCut);
1537
1538 $(renderer, drawPolygonFilled, points, lengthof(points), self->backgroundGradientAngle,
1539 &self->backgroundColor, gradient);
1540
1541 } else if (gradient) {
1542 $(renderer, drawRoundedRectGradientFilled, &frame, self->borderRadius,
1543 self->backgroundGradientAngle, &self->backgroundColor, gradient);
1544 } else if (self->borderRadius > 0) {
1545 $(renderer, drawRoundedRectFilled, &frame, self->borderRadius, &self->backgroundColor);
1546 } else {
1547 $(renderer, drawRectFilled, &frame, &self->backgroundColor);
1548 }
1549 }
1550
1551 if (self->borderWidth && self->borderColor.a) {
1552
1553 SDL_Rect frame = $(self, renderFrame);
1554
1555 if (cut) {
1556
1557 // The stroke is centered on the outline, so grow by half of it to sit outside the fill
1558 const int inset = self->borderWidth / 2;
1559
1560 frame.x -= inset;
1561 frame.y -= inset;
1562 frame.w += inset * 2;
1563 frame.h += inset * 2;
1564
1565 SDL_Point points[4];
1566 cornerCutPoints(points, &frame, &self->cornerCut);
1567
1568 $(renderer, drawPolygon, points, lengthof(points), self->borderWidth, &self->borderColor);
1569
1570 } else if (self->borderRadius > 0) {
1571
1572 // The border grows outward, so its inner edge shares the background's radius
1573 frame.x -= self->borderWidth;
1574 frame.y -= self->borderWidth;
1575 frame.w += self->borderWidth * 2;
1576 frame.h += self->borderWidth * 2;
1577
1578 $(renderer, drawRoundedRect, &frame, self->borderRadius + self->borderWidth, self->borderWidth, &self->borderColor);
1579
1580 } else {
1581
1582 for (int i = 0; i < self->borderWidth; i++) {
1583
1584 $(renderer, drawRect, &frame, &self->borderColor);
1585
1586 frame.x -= 1;
1587 frame.y -= 1;
1588 frame.w += 2;
1589 frame.h += 2;
1590 }
1591 }
1592 }
1593}
1594
1598static void renderDeviceDidReset_enumerate(View *subview, ident data) {
1599 $(subview, renderDeviceDidReset);
1600}
1601
1606static void renderDeviceDidReset(View *self) {
1608}
1609
1613static void renderDeviceWillReset_enumerate(View *subview, ident data) {
1614 $(subview, renderDeviceWillReset);
1615}
1616
1621static void renderDeviceWillReset(View *self) {
1623}
1624
1629static SDL_Rect renderFrame(const View *self) {
1630
1631 View *this = (View *) self;
1632
1633 if (_renderFrameGeneration && this->renderFrameCache.generation == _renderFrameGeneration) {
1634 return this->renderFrameCache.frame;
1635 }
1636
1637 SDL_Rect frame = self->frame;
1638
1639 const View *superview = self->superview;
1640 if (superview) {
1641
1642 const SDL_Rect superFrame = $(superview, renderFrame);
1643
1644 frame.x += superFrame.x;
1645 frame.y += superFrame.y;
1646
1647 if (self->alignment != ViewAlignmentInternal) {
1648 frame.x += superview->padding.left;
1649 frame.y += superview->padding.top;
1650 }
1651 }
1652
1653 this->renderFrameCache.frame = frame;
1654 this->renderFrameCache.generation = _renderFrameGeneration;
1655
1656 return frame;
1657}
1658
1663static void replaceSubview(View *self, View *subview, View *replacement) {
1664
1665 assert(subview);
1666 assert(replacement);
1667
1668 $(self, addSubviewRelativeTo, replacement, subview, ViewPositionAfter);
1669 $(self, removeSubview, subview);
1670}
1671
1676static void resignKeyResponder(View *self) {
1677
1678 if (self->window && $(self, isKeyResponder)) {
1679
1680 String *path = $(self, path);
1681 MVC_LogDebug("%s\n", path->chars);
1682 release(path);
1683
1684 SDL_SetPointerProperty(SDL_GetWindowProperties(self->window), "keyResponder", NULL);
1685 }
1686}
1687
1692static void resignTouchResponder(View *self) {
1693
1694 if (self->window && $(self, isTouchResponder)) {
1695
1696 String *path = $(self, path);
1697 MVC_LogDebug("%s\n", path->chars);
1698 release(path);
1699
1700 SDL_SetPointerProperty(SDL_GetWindowProperties(self->window), "touchResponder", NULL);
1701 }
1702}
1703
1708static void resize(View *self, const SDL_Size *size) {
1709
1710 const int w = ClampSize(size->w, self->minSize.w, self->maxSize.w);
1711 const int h = ClampSize(size->h, self->minSize.h, self->maxSize.h);
1712
1713 if (self->frame.w != w || self->frame.h != h) {
1714
1715 self->frame.w = w;
1716 self->frame.h = h;
1717
1718 $(self, setNeedsLayout);
1719
1720 if (self->superview) {
1721 if ($(self->superview, isContainer) || self->alignment & ViewAlignmentMaskAny || self->stretch) {
1722 $(self->superview, setNeedsLayout);
1723 }
1724 }
1725 }
1726}
1727
1732static void resolve(View *self, Outlet *outlets) {
1733
1734 if (outlets) {
1735 for (Outlet *outlet = outlets; outlet->identifier; outlet++) {
1736 *outlet->view = $(self, descendantWithIdentifier, outlet->identifier);
1737 if (*outlet->view == NULL) {
1738 String *desc = $((Object *) self, description);
1739 MVC_LogError("Failed to resolve outlet '%s' for %s", outlet->identifier, desc->chars);
1740 release(desc);
1741 }
1742
1743 assert(*outlet->view);
1744 }
1745 }
1746}
1747
1752static void respondToEvent(View *self, const SDL_Event *event) {
1753
1754 assert(event);
1755
1756 ViewEvent code = ViewEventNone;
1757
1758 switch (event->type) {
1759 case SDL_EVENT_MOUSE_BUTTON_DOWN:
1761 break;
1762 case SDL_EVENT_MOUSE_BUTTON_UP:
1764 $(self, resignTouchResponder);
1765 break;
1766 case SDL_EVENT_KEY_DOWN:
1767 code = ViewEventKeyDown;
1768 break;
1769 case SDL_EVENT_KEY_UP:
1770 code = ViewEventKeyUp;
1771 break;
1772 default:
1773 break;
1774 }
1775
1776 if (code != ViewEventNone) {
1777 $(self, emitViewEvent, code, NULL);
1778
1779 if (code == ViewEventMouseButtonUp) {
1780 if (event->button.clicks) {
1781 $(self, emitViewEvent, ViewEventClick, NULL);
1782 }
1783 }
1784 }
1785
1786 if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
1787 if ($(self, acceptsKeyResponder)) {
1788 $(self, becomeKeyResponder);
1789 return;
1790 }
1791 }
1792
1793 if (self->nextResponder) {
1794 $(self->nextResponder, respondToEvent, event);
1795 } else if (self->superview) {
1796 $(self->superview, respondToEvent, event);
1797 }
1798}
1799
1804static Set *_select(View *self, const char *rule) {
1805
1806 Selector *selector = $(alloc(Selector), initWithRule, rule);
1807 assert(selector);
1808
1809 Set *selection = $(selector, select, self);
1810
1811 release(selector);
1812 return selection;
1813}
1814
1819static View *selectFirst(View *self, const char *rule) {
1820
1821 Selector *selector = $(alloc(Selector), initWithRule, rule);
1822 assert(selector);
1823
1824 View *first = $(selector, selectFirst, self);
1825
1826 release(selector);
1827 return first;
1828}
1829
1834static void setVisibility(View *self, ViewVisibility visibility) {
1835
1836 if (self->visibility != visibility) {
1837
1838 self->visibility = visibility;
1839
1840 $(self->style, addEnumAttribute, "visibility", ViewVisibilityNames, visibility);
1841
1842 if (self->superview && $(self->superview, isContainer)) {
1843 $(self->superview, setNeedsLayout);
1844 }
1845 }
1846}
1847
1852static void setNeedsApplyTheme(View *self) {
1853
1854 self->needsApplyTheme = true;
1855
1856 for (View *view = self->superview; view && !view->needsApplyThemeSubviews; view = view->superview) {
1857 view->needsApplyThemeSubviews = true;
1858 }
1859}
1860
1865static void setNeedsLayout(View *self) {
1866
1867 self->needsLayout = true;
1868
1869 for (View *view = self->superview; view && !view->needsLayoutSubviews; view = view->superview) {
1870 view->needsLayoutSubviews = true;
1871 }
1872}
1873
1878static SDL_Size size(const View *self) {
1879 return MakeSize(self->frame.w, self->frame.h);
1880}
1881
1886static SDL_Size sizeThatContains(const View *self) {
1887
1888 const SDL_Size size = $(self, size);
1889 const SDL_Size sizeThatFits = $(self, sizeThatFits);
1890
1891 return MakeSize(max(size.w, sizeThatFits.w), max(size.h, sizeThatFits.h));
1892}
1893
1898static SDL_Size sizeThatFills(const View *self) {
1899 SDL_Size size;
1900
1901 if (self->superview == NULL) {
1902 SDL_GetWindowSize(self->window, &size.w, &size.h);
1903 } else {
1904 size = $(self->superview, size);
1905 }
1906
1907 return size;
1908}
1909
1914static SDL_Size sizeThatFits(const View *self) {
1915
1916 SDL_Size size = $(self, size);
1917
1918 if ($(self, isContainer)) {
1919 size = MakeSize(0, 0);
1920
1921 Array *subviews = $(self, visibleSubviews);
1922 for (size_t i = 0; i < subviews->count; i++) {
1923
1924 View *subview = subviews->elements[i];
1925
1926 const ViewConstraint unspecified = MakeConstraint(ViewConstraintUnspecified, 0);
1927 const SDL_Size subviewSize = $(subview, sizeThatSatisfies, unspecified, unspecified);
1928
1929 SDL_Point subviewOrigin = MakePoint(0, 0);
1930 switch (subview->alignment) {
1931 case ViewAlignmentNone:
1932 subviewOrigin = MakePoint(subview->frame.x, subview->frame.y);
1933 break;
1934 default:
1935 break;
1936 }
1937
1938 size.w = max(size.w, subviewOrigin.x + subviewSize.w);
1939 size.h = max(size.h, subviewOrigin.y + subviewSize.h);
1940 }
1941
1942 size.w += self->padding.left + self->padding.right;
1943 size.h += self->padding.top + self->padding.bottom;
1944
1945 release(subviews);
1946
1947 }
1948
1949 size.w = ClampSize(size.w, self->minSize.w, self->maxSize.w);
1950 size.h = ClampSize(size.h, self->minSize.h, self->maxSize.h);
1951
1952 return size;
1953}
1954
1959static int resolveViewConstraint(int hasAutoresizingBit, ViewConstraint constraint, int intrinsic) {
1960
1961 if (!hasAutoresizingBit) {
1962 return intrinsic;
1963 }
1964
1965 switch (constraint.mode) {
1966 case ViewConstraintEqual:
1967 return constraint.value;
1968 case ViewConstraintMax:
1969 return min(intrinsic, constraint.value);
1970 default:
1971 return intrinsic;
1972 }
1973}
1974
1979static SDL_Size sizeThatSatisfies(View *self, ViewConstraint width, ViewConstraint height) {
1980
1981 SDL_Size size = $(self, sizeThatFits);
1982
1985
1986 size.w = ClampSize(size.w, self->minSize.w, self->maxSize.w);
1987 size.h = ClampSize(size.h, self->minSize.h, self->maxSize.h);
1988
1989 return size;
1990}
1991
1996static void sizeToContain(View *self) {
1997
1998 const SDL_Size size = $(self, sizeThatContains);
1999
2000 $(self, resize, &size);
2001}
2002
2007static void sizeToFill(View *self) {
2008
2009 const SDL_Size size = $(self, sizeThatFills);
2010
2011 $(self, resize, &size);
2012}
2013
2018static void sizeToFit(View *self) {
2019
2020 const SDL_Size size = $(self, sizeThatFits);
2021
2022 $(self, resize, &size);
2023}
2024
2029static void sizeToSatisfy(View *self, ViewConstraint width, ViewConstraint height) {
2030
2031 const SDL_Size size = $(self, sizeThatSatisfies, width, height);
2032
2033 $(self, resize, &size);
2034}
2035
2040static View *subviewWithIdentifier(const View *self, const char *identifier) {
2041
2042 assert(identifier);
2043
2044 const Array *subviews = (Array *) self->subviews;
2045 for (size_t i = 0; i < subviews->count; i++) {
2046
2047 View *subview = subviews->elements[i];
2048 if (subview->identifier) {
2049
2050 if (strcmp(identifier, subview->identifier) == 0) {
2051 return subview;
2052 }
2053 }
2054 }
2055
2056 return NULL;
2057}
2058
2062static void updateBindings_enumerate(View *subview, ident data) {
2063 $(subview, updateBindings, data);
2064}
2065
2070static void updateBindings(View *self, ident data) {
2072}
2073
2078static SDL_Rect viewport(const View *self) {
2079
2080 assert(self->window);
2081
2082 const SDL_Rect frame = $(self, renderFrame);
2083
2084 return MVC_TransformToWindow(self->window, &frame);
2085}
2086
2091static View *viewWithCharacters(const char *chars, Outlet *outlets) {
2092
2093 Data *data = $$(Data, dataWithConstMemory, (ident) chars, strlen(chars));
2094
2095 View *view = $$(View, viewWithData, data, outlets);
2096
2097 release(data);
2098
2099 return view;
2100}
2101
2106static View *viewWithData(const Data *data, Outlet *outlets) {
2107
2108 JSONContext *ctx = $(alloc(JSONContext), init);
2109 Dictionary *dictionary = $(ctx, objectFromData, data, 0);
2110
2111 View *view = NULL;
2112 if (dictionary) {
2113 view = $$(View, viewWithDictionary, dictionary, outlets);
2114 release(dictionary);
2115 } else {
2116 MVC_LogError("Failed to parse JSON\n");
2117 }
2118
2119 release(ctx);
2120 return view;
2121}
2122
2127static View *viewWithDictionary(const Dictionary *dictionary, Outlet *outlets) {
2128
2129 View *view = NULL;
2130
2131 BindInlet(&MakeInlet(NULL, InletTypeView, &view, NULL), dictionary);
2132
2133 $(view, resolve, outlets);
2134
2135 return view;
2136}
2137
2142static View *viewWithResource(const Resource *resource, Outlet *outlets) {
2143
2144 assert(resource);
2145
2146 return $$(View, viewWithData, resource->data, outlets);
2147}
2148
2153static View *viewWithResourceName(const char *name, Outlet *outlets) {
2154
2155 Resource *resource = $$(Resource, resourceWithName, name);
2156
2157 View *view = $$(View, viewWithResource, resource, outlets);
2158
2159 release(resource);
2160
2161 return view;
2162}
2163
2167static bool visibleSubviews_filter(ident obj, ident data) {
2168
2169 const View *view = (View *) obj;
2170
2172}
2173
2178static Array *visibleSubviews(const View *self) {
2179 return $((Array *) self->subviews, filteredArray, visibleSubviews_filter, NULL);
2180}
2181
2186static void warn(View *self, WarningType type, const char *fmt, ...) {
2187
2188 va_list args;
2189 va_start(args, fmt);
2190
2191 Warning *warning = $(alloc(Warning), initWithVaList, type, fmt, args);
2192
2193 va_end(args);
2194
2195 String *description = $((Object *) self, description);
2196 MVC_LogWarn("%s:: %s\n", description->chars, warning->message->chars);
2197 release(description);
2198
2199 $(self->warnings, addObject, warning);
2200
2201 release(warning);
2202}
2203
2208static void willMoveToWindow(View *self, SDL_Window *window) {
2209
2210 if (self->window) {
2211 $(self, resignKeyResponder);
2212 $(self, resignTouchResponder);
2213 $(self, detachStylesheet, self->window);
2214 }
2215}
2216
2217#pragma mark - View class methods
2218
2222static void initialize(Class *clazz) {
2223
2224 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
2225 ((ObjectInterface *) clazz->interface)->description = description;
2226
2227 ((ViewInterface *) clazz->interface)->acceptsKeyResponder = acceptsKeyResponder;
2228 ((ViewInterface *) clazz->interface)->acceptsTouchResponder = acceptsTouchResponder;
2229 ((ViewInterface *) clazz->interface)->addClassName = addClassName;
2230 ((ViewInterface *) clazz->interface)->addSubview = addSubview;
2231 ((ViewInterface *) clazz->interface)->addSubviewRelativeTo = addSubviewRelativeTo;
2232 ((ViewInterface *) clazz->interface)->ancestorWithIdentifier = ancestorWithIdentifier;
2233 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
2234 ((ViewInterface *) clazz->interface)->applyTheme = applyTheme;
2235 ((ViewInterface *) clazz->interface)->applyThemeIfNeeded = applyThemeIfNeeded;
2236 ((ViewInterface *) clazz->interface)->attachStylesheet = attachStylesheet;
2237 ((ViewInterface *) clazz->interface)->awakeWithCharacters = awakeWithCharacters;
2238 ((ViewInterface *) clazz->interface)->awakeWithData = awakeWithData;
2239 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
2240 ((ViewInterface *) clazz->interface)->awakeWithResource = awakeWithResource;
2241 ((ViewInterface *) clazz->interface)->awakeWithResourceName = awakeWithResourceName;
2242 ((ViewInterface *) clazz->interface)->becomeKeyResponder = becomeKeyResponder;
2243 ((ViewInterface *) clazz->interface)->becomeTouchResponder = becomeTouchResponder;
2244 ((ViewInterface *) clazz->interface)->bind = _bind;
2245 ((ViewInterface *) clazz->interface)->bounds = bounds;
2246 ((ViewInterface *) clazz->interface)->bringSubviewToFront = bringSubviewToFront;
2247 ((ViewInterface *) clazz->interface)->clearWarnings = clearWarnings;
2248 ((ViewInterface *) clazz->interface)->clippingFrame = clippingFrame;
2249 ((ViewInterface *) clazz->interface)->containsPoint = containsPoint;
2250 ((ViewInterface *) clazz->interface)->depth = depth;
2251 ((ViewInterface *) clazz->interface)->descendantWithIdentifier = descendantWithIdentifier;
2252 ((ViewInterface *) clazz->interface)->detachStylesheet = detachStylesheet;
2253 ((ViewInterface *) clazz->interface)->didMoveToWindow = didMoveToWindow;
2254 ((ViewInterface *) clazz->interface)->didReceiveEvent = didReceiveEvent;
2255 ((ViewInterface *) clazz->interface)->draw = draw;
2256 ((ViewInterface *) clazz->interface)->emitViewEvent = emitViewEvent;
2257 ((ViewInterface *) clazz->interface)->enumerate = enumerate;
2258 ((ViewInterface *) clazz->interface)->enumerateAdjacent = enumerateAdjacent;
2259 ((ViewInterface *) clazz->interface)->enumerateAncestors = enumerateAncestors;
2260 ((ViewInterface *) clazz->interface)->enumerateDescendants = enumerateDescendants;
2261 ((ViewInterface *) clazz->interface)->enumerateSelection = enumerateSelection;
2262 ((ViewInterface *) clazz->interface)->enumerateSiblings = enumerateSiblings;
2263 ((ViewInterface *) clazz->interface)->enumerateSubviews = enumerateSubviews;
2264 ((ViewInterface *) clazz->interface)->enumerateSuperview = enumerateSuperview;
2265 ((ViewInterface *) clazz->interface)->enumerateVisible = enumerateVisible;
2266 ((ViewInterface *) clazz->interface)->hasClassName = hasClassName;
2267 ((ViewInterface *) clazz->interface)->hasOverflow = hasOverflow;
2268 ((ViewInterface *) clazz->interface)->hitTest = hitTest;
2269 ((ViewInterface *) clazz->interface)->init = init;
2270 ((ViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
2271 ((ViewInterface *) clazz->interface)->invalidateStyle = invalidateStyle;
2272 ((ViewInterface *) clazz->interface)->isContainer = isContainer;
2273 ((ViewInterface *) clazz->interface)->isDescendantOfView = isDescendantOfView;
2274 ((ViewInterface *) clazz->interface)->isKeyResponder = isKeyResponder;
2275 ((ViewInterface *) clazz->interface)->isTouchResponder = isTouchResponder;
2276 ((ViewInterface *) clazz->interface)->isVisible = isVisible;
2277 ((ViewInterface *) clazz->interface)->layoutIfNeeded = layoutIfNeeded;
2278 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
2279 ((ViewInterface *) clazz->interface)->layoutWithConstraint = layoutWithConstraint;
2280 ((ViewInterface *) clazz->interface)->layoutWithSize = layoutWithSize;
2281 ((ViewInterface *) clazz->interface)->matchesSelector = matchesSelector;
2282 ((ViewInterface *) clazz->interface)->moveToWindow = moveToWindow;
2283 ((ViewInterface *) clazz->interface)->path = path;
2284 ((ViewInterface *) clazz->interface)->removeAllClassNames = removeAllClassNames;
2285 ((ViewInterface *) clazz->interface)->removeAllSubviews = removeAllSubviews;
2286 ((ViewInterface *) clazz->interface)->removeClassName = removeClassName;
2287 ((ViewInterface *) clazz->interface)->removeFromSuperview = removeFromSuperview;
2288 ((ViewInterface *) clazz->interface)->removeSubview = removeSubview;
2289 ((ViewInterface *) clazz->interface)->render = render;
2290 ((ViewInterface *) clazz->interface)->renderDeviceDidReset = renderDeviceDidReset;
2291 ((ViewInterface *) clazz->interface)->renderDeviceWillReset = renderDeviceWillReset;
2292 ((ViewInterface *) clazz->interface)->renderFrame = renderFrame;
2293 ((ViewInterface *) clazz->interface)->replaceSubview = replaceSubview;
2294 ((ViewInterface *) clazz->interface)->resignKeyResponder = resignKeyResponder;
2295 ((ViewInterface *) clazz->interface)->resignTouchResponder = resignTouchResponder;
2296 ((ViewInterface *) clazz->interface)->resize = resize;
2297 ((ViewInterface *) clazz->interface)->resolve = resolve;
2298 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
2299 ((ViewInterface *) clazz->interface)->select = _select;
2300 ((ViewInterface *) clazz->interface)->selectFirst = selectFirst;
2301 ((ViewInterface *) clazz->interface)->setVisibility = setVisibility;
2302 ((ViewInterface *) clazz->interface)->setNeedsApplyTheme = setNeedsApplyTheme;
2303 ((ViewInterface *) clazz->interface)->setNeedsLayout = setNeedsLayout;
2304 ((ViewInterface *) clazz->interface)->size = size;
2305 ((ViewInterface *) clazz->interface)->sizeThatContains = sizeThatContains;
2306 ((ViewInterface *) clazz->interface)->sizeThatFills = sizeThatFills;
2307 ((ViewInterface *) clazz->interface)->sizeThatFits = sizeThatFits;
2308 ((ViewInterface *) clazz->interface)->sizeThatSatisfies = sizeThatSatisfies;
2309 ((ViewInterface *) clazz->interface)->sizeToContain = sizeToContain;
2310 ((ViewInterface *) clazz->interface)->sizeToFill = sizeToFill;
2311 ((ViewInterface *) clazz->interface)->sizeToFit = sizeToFit;
2312 ((ViewInterface *) clazz->interface)->sizeToSatisfy = sizeToSatisfy;
2313 ((ViewInterface *) clazz->interface)->subviewWithIdentifier = subviewWithIdentifier;
2314 ((ViewInterface *) clazz->interface)->updateBindings = updateBindings;
2315 ((ViewInterface *) clazz->interface)->viewport = viewport;
2316 ((ViewInterface *) clazz->interface)->viewWithCharacters = viewWithCharacters;
2317 ((ViewInterface *) clazz->interface)->viewWithData = viewWithData;
2318 ((ViewInterface *) clazz->interface)->viewWithDictionary = viewWithDictionary;
2319 ((ViewInterface *) clazz->interface)->viewWithResource = viewWithResource;
2320 ((ViewInterface *) clazz->interface)->viewWithResourceName = viewWithResourceName;
2321 ((ViewInterface *) clazz->interface)->visibleSubviews = visibleSubviews;
2322 ((ViewInterface *) clazz->interface)->warn = warn;
2323 ((ViewInterface *) clazz->interface)->willMoveToWindow = willMoveToWindow;
2324
2325 MVC_NOTIFICATION_EVENT = SDL_RegisterEvents(1);
2326 assert(MVC_NOTIFICATION_EVENT != 0);
2327
2328 MVC_VIEW_EVENT = SDL_RegisterEvents(1);
2329 assert(MVC_VIEW_EVENT != 0);
2330}
2331
2336Class *_View(void) {
2337 static Class *clazz;
2338 static Once once;
2339
2340 do_once(&once, {
2341 clazz = _initialize(&(const ClassDef) {
2342 .name = "View",
2343 .superclass = _Object(),
2344 .instanceSize = sizeof(View),
2345 .interfaceSize = sizeof(ViewInterface),
2346 .initialize = initialize,
2347 });
2348 });
2349
2350 return clazz;
2351}
2352
2353#undef _Class
static String * name(const Font *self)
Definition Font.c:303
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
Definition Log.h:54
#define MVC_LogDebug(fmt,...)
Definition Log.h:48
#define MVC_LogError(fmt,...)
Definition Log.h:57
static void drawView(Renderer *self, View *view)
Definition Renderer.c:525
static void drawRoundedRectGradientFilled(const Renderer *self, const SDL_Rect *rect, int radius, int angle, const SDL_Color *from, const SDL_Color *to)
Definition Renderer.c:441
static void drawPolygon(const Renderer *self, const SDL_Point *points, size_t count, int width, const SDL_Color *color)
Definition Renderer.c:293
static void drawRoundedRect(const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *color)
Definition Renderer.c:407
static void drawRectFilled(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:384
static void drawRect(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:365
static void drawRoundedRectFilled(const Renderer *self, const SDL_Rect *rect, int radius, const SDL_Color *color)
Definition Renderer.c:424
static void drawPolygonFilled(const Renderer *self, const SDL_Point *points, size_t count, int angle, const SDL_Color *from, const SDL_Color *to)
Definition Renderer.c:310
static Selector * initWithRule(Selector *self, const char *rule)
Definition Selector.c:190
@ SimpleSelectorTypePseudo
@ SimpleSelectorTypeId
@ SimpleSelectorTypeClass
@ SimpleSelectorTypeUniversal
@ SimpleSelectorTypeType
@ SimpleSelectorTypeNone
static bool isComputedEqual(const Style *self, const Style *other)
Definition Style.c:433
static void addEnumAttribute(Style *self, const char *attr, const EnumName *names, int value)
Definition Style.c:288
static Style * initWithAttributes(Style *self, const Dictionary *attributes)
Definition Style.c:394
static void addAttributes(Style *self, const Dictionary *attributes)
Definition Style.c:221
static Theme * theme(SDL_Window *window)
Definition Theme.c:295
static void addStylesheet(Theme *self, Stylesheet *stylesheet)
Definition Theme.c:59
static Style * computeStyle(const Theme *self, const View *view)
Definition Theme.c:223
static void removeStylesheet(Theme *self, Stylesheet *stylesheet)
Definition Theme.c:261
ViewEvent
View event relay codes.
Definition Types.h:61
@ ViewEventMouseButtonUp
A View has received a mouse button up event.
Definition Types.h:85
@ ViewEventMouseButtonDown
A View has received a mouse button down event.
Definition Types.h:80
@ ViewEventNone
No event.
Definition Types.h:65
@ ViewEventKeyUp
A View has received a key up event.
Definition Types.h:75
@ ViewEventClick
A Control received one or more click events.
Definition Types.h:115
@ ViewEventKeyDown
A View has received a key down event.
Definition Types.h:70
void(* ViewEnumerator)(View *view, ident data)
A function type for View enumeration.
Definition Types.h:56
bool bindInlets(const Inlet *inlets, const Dictionary *dictionary)
Binds each Inlet specified in inlets to the data provided in dictionary.
Definition View+JSON.c:303
@ InletTypeSubviews
Definition View+JSON.h:116
@ InletTypeStyle
Definition View+JSON.h:110
@ 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:89
@ InletTypeColor
Definition View+JSON.h:62
@ InletTypeView
Definition View+JSON.h:133
@ InletTypeSize
Definition View+JSON.h:104
@ InletTypeRectangle
Definition View+JSON.h:99
#define BindInlet(inlet, obj)
Binds the Inlet to obj by invoking the appropriate InletBinding function.
Definition View+JSON.h:238
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
Definition View+JSON.h:215
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
Definition View+JSON.h:210
const EnumName ViewAlignmentNames[]
Definition View.c:63
static SDL_Size size(const View *self)
Definition View.c:1878
static View * viewWithResource(const Resource *resource, Outlet *outlets)
Definition View.c:2142
static void removeClassName(View *self, const char *className)
Definition View.c:1445
static void addClassName(View *self, const char *className)
Definition View.c:183
static View * selectFirst(View *self, const char *rule)
Definition View.c:1819
static bool isContainer(const View *self)
Definition View.c:1100
static void addSubviewRelativeTo(View *self, View *subview, View *other, ViewPosition position)
Definition View.c:209
static void didMoveToWindow(View *self, SDL_Window *window)
Definition View.c:709
static bool isDescendantOfView(const View *self, const View *view)
Definition View.c:1108
static bool filterViewEvents(void *data, SDL_Event *event)
Definition View.c:99
static void becomeKeyResponder(View *self)
Definition View.c:487
static String * path(const View *self)
Definition View.c:1378
static void resize(View *self, const SDL_Size *size)
Definition View.c:1708
static SDL_Size sizeThatSatisfies(View *self, ViewConstraint width, ViewConstraint height)
Definition View.c:1979
static void detachStylesheet(View *self, SDL_Window *window)
Definition View.c:693
static bool isTouchResponder(const View *self)
Definition View.c:1139
const EnumName ViewPointerEventsNames[]
Definition View.c:52
static void layoutIfNeeded(View *self)
Definition View.c:1174
static void sizeToContain(View *self)
Definition View.c:1996
static bool acceptsKeyResponder(const View *self)
Definition View.c:167
Class * _View(void)
Definition View.c:2336
static int resolveViewConstraint(int hasAutoresizingBit, ViewConstraint constraint, int intrinsic)
Resolves an axis's sizeThatFits value against an offered ViewConstraint, if the View has opted into t...
Definition View.c:1959
static View * init(View *self)
Definition View.c:1039
static Set * _select(View *self, const char *rule)
Definition View.c:1804
static void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:840
static void clearWarnings(const View *self, WarningType type)
Definition View.c:598
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition View.c:445
static int depth(const View *self)
Definition View.c:658
static void enumerateVisible(View *self, ViewEnumerator enumerator, ident data)
Definition View.c:931
static void draw(View *self, Renderer *renderer)
Definition View.c:778
static SDL_Size sizeThatFills(const View *self)
Definition View.c:1898
static void removeAllClassNames(View *self)
Definition View.c:1419
static void removeSubview(View *self, View *subview)
Definition View.c:1473
static void nullifySuperview(View *subview, ident data)
ViewEnumerator to null out superview pointers during parent dealloc.
Definition View.c:111
static void updateBindings_enumerate(View *subview, ident data)
ViewEnumerator for updateBindings recursion.
Definition View.c:2062
static void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:904
static void layoutWithConstraint(View *self, ViewConstraint width, ViewConstraint height)
Definition View.c:1254
static void cornerCutPoints(SDL_Point *points, const SDL_Rect *rect, const ViewCornerCut *cut)
Writes the four corners of rect, inset by cut, in clockwise order.
Definition View.c:1496
void MVC_InvalidateRenderFrames(void)
Invalidates every View's cached View::renderFrame and View::clippingFrame.
Definition View.c:46
static bool visibleSubviews_filter(ident obj, ident data)
Predicate for visibleSubviews.
Definition View.c:2167
static View * viewWithResourceName(const char *name, Outlet *outlets)
Definition View.c:2153
static void becomeTouchResponder(View *self)
Definition View.c:511
static void removeAllSubviews_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for removeAllSubviews.
Definition View.c:1429
static void enumerateSelection(View *self, const char *rule, ViewEnumerator enumerator, ident data)
Definition View.c:853
static void respondToEvent(View *self, const SDL_Event *event)
Definition View.c:1752
static void applyStyle(View *self, const Style *style)
Definition View.c:272
static bool isVisible(const View *self)
Definition View.c:1152
static void resignTouchResponder(View *self)
Definition View.c:1692
static void replaceSubview(View *self, View *subview, View *replacement)
Definition View.c:1663
static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
Definition View.c:1280
static SDL_Size sizeThatFits(const View *self)
Definition View.c:1914
static void bringSubviewToFront(View *self, View *subview)
Definition View.c:570
static bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition View.c:726
static View * subviewWithIdentifier(const View *self, const char *identifier)
Definition View.c:2040
static void _draw(View *view, ident data)
ViewEnumerator adapter for draw.
Definition View.c:774
Uint32 MVC_VIEW_EVENT
Definition View.c:37
static String * description(const Object *self)
Definition View.c:145
static bool hasOverflow(const View *self)
Definition View.c:997
static Array * visibleSubviews(const View *self)
Definition View.c:2178
static void invalidateStyle(View *self)
Definition View.c:1092
static void sizeToFill(View *self)
Definition View.c:2007
static void sizeToSatisfy(View *self, ViewConstraint width, ViewConstraint height)
Definition View.c:2029
static void warn(View *self, WarningType type, const char *fmt,...)
Definition View.c:2186
static void setNeedsLayout(View *self)
Definition View.c:1865
static void awakeWithCharacters(View *self, const char *chars)
Definition View.c:413
static View * descendantWithIdentifier(const View *self, const char *identifier)
Definition View.c:666
static void moveToWindow(View *self, SDL_Window *window)
Definition View.c:1351
static View * initWithFrame(View *self, const SDL_Rect *frame)
Definition View.c:1047
static void removeFromSuperview(View *self)
Definition View.c:1462
static void updateBindings(View *self, ident data)
Definition View.c:2070
static bool hasClassName_predicate(const ident obj, ident data)
Predicate for hasClassName.
Definition View.c:950
static unsigned _renderFrameGeneration
The render frame generation; per-View renderFrame and clippingFrame caches are valid only while their...
Definition View.c:44
static void removeAllSubviews(View *self)
Definition View.c:1437
static SDL_Size sizeThatContains(const View *self)
Definition View.c:1886
static void applyTheme(View *self, const Theme *theme)
Definition View.c:335
static void render(View *self, Renderer *renderer)
Definition View.c:1519
static void setVisibility(View *self, ViewVisibility visibility)
Definition View.c:1834
static void moveToWindow_enumerate(View *subview, ident data)
ViewEnumerator for moveToWindow recursion.
Definition View.c:1343
static void awakeWithResourceName(View *self, const char *name)
Definition View.c:474
static void layoutWithSize(View *self, const SDL_Size *size)
Definition View.c:1267
static void setNeedsApplyTheme(View *self)
Definition View.c:1852
static void dealloc(Object *self)
Definition View.c:118
static void invalidateStyle_enumerate(View *view, ident data)
ViewEnumerator for invalidateStyle.
Definition View.c:1081
static void addSubview(View *self, View *subview)
Definition View.c:201
static SDL_Rect renderFrame(const View *self)
Definition View.c:1629
static void renderDeviceWillReset_enumerate(View *subview, ident data)
ViewEnumerator for renderDeviceWillReset recursion.
Definition View.c:1613
static void resignKeyResponder(View *self)
Definition View.c:1676
static View * viewWithData(const Data *data, Outlet *outlets)
Definition View.c:2106
static View * viewWithCharacters(const char *chars, Outlet *outlets)
Definition View.c:2091
static void initialize(Class *clazz)
Definition View.c:2222
const EnumName ViewAutoresizingNames[]
Definition View.c:83
static bool acceptsTouchResponder(const View *self)
Definition View.c:175
static void renderDeviceWillReset(View *self)
Definition View.c:1621
static bool _bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
Definition View.c:535
static bool clearWarnings_predicate(const ident obj, ident data)
Filter Predicate for clearWarnings.
Definition View.c:586
static void _applyThemeIfNeeded(View *view, ident data)
ViewEnumerator adapter for applyThemeIfNeeded.
Definition View.c:365
static void willMoveToWindow(View *self, SDL_Window *window)
Definition View.c:2208
Uint32 MVC_NOTIFICATION_EVENT
Definition View.c:36
static void attachStylesheet(View *self, SDL_Window *window)
Definition View.c:397
static bool isKeyResponder(const View *self)
Definition View.c:1126
static void renderDeviceDidReset(View *self)
Definition View.c:1606
static void awakeWithResource(View *self, const Resource *resource)
Definition View.c:463
static void hasOverflow_enumerate(View *view, ident data)
ViewEnumerator for hasOverflow.
Definition View.c:975
static void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:918
static void renderDeviceDidReset_enumerate(View *subview, ident data)
ViewEnumerator for renderDeviceDidReset recursion.
Definition View.c:1598
static void layoutSubviews(View *self)
Definition View.c:1207
static void enumerate(View *self, ViewEnumerator enumerator, ident data)
Definition View.c:807
static void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:820
static SDL_Rect viewport(const View *self)
Definition View.c:2078
static void layoutIfNeeded_enumerate(View *subview, ident data)
ViewEnumerator for layoutIfNeeded recursion.
Definition View.c:1166
static View * hitTest(const View *self, const SDL_Point *point)
Definition View.c:1012
static bool hasClassName(const View *self, const char *className)
Definition View.c:958
static void applyThemeIfNeeded(View *self, const Theme *theme)
Definition View.c:369
static bool containsPoint(const View *self, const SDL_Point *point)
Definition View.c:647
static void sizeToFit(View *self)
Definition View.c:2018
static SDL_Rect clippingFrame(const View *self)
Definition View.c:606
static void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:884
const EnumName ViewVisibilityNames[]
Definition View.c:57
static View * viewWithDictionary(const Dictionary *dictionary, Outlet *outlets)
Definition View.c:2127
static void awakeWithData(View *self, const Data *data)
Definition View.c:426
static void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:866
static SDL_Rect bounds(const View *self)
Definition View.c:552
static View * ancestorWithIdentifier(const View *self, const char *identifier)
Definition View.c:251
static void resolve(View *self, Outlet *outlets)
Definition View.c:1732
static void emitViewEvent(View *self, ViewEvent code, ident data)
Definition View.c:794
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
@ ViewPointerEventsNone
Definition View.h:111
@ ViewPointerEventsAuto
Definition View.h:110
#define ViewAlignmentMaskBottom
Definition View.h:46
#define ViewAlignmentMaskTop
Definition View.h:44
#define ViewAlignmentMaskMiddle
Definition View.h:45
#define CornerCutIsEmpty(cut)
Definition View.h:204
ViewVisibility
Visibility constants.
Definition View.h:119
@ ViewVisibilityUnspecified
Definition View.h:120
@ ViewVisibilityVisible
Definition View.h:121
@ ViewVisibilityHidden
Definition View.h:122
#define MakeConstraint(mode, value)
Definition View.h:163
#define ViewAlignmentMaskHorizontal
Definition View.h:55
#define ClampSize(size, min, max)
Clamps size to min and max, where a max of zero is unbounded.
Definition View.h:103
ViewPosition
Relative positioning of subviews within their superview.
Definition View.h:210
@ ViewPositionAfter
Definition View.h:212
#define ViewAlignmentMaskVertical
Definition View.h:52
#define ViewAlignmentMaskAny
Definition View.h:58
@ ViewAutoresizingNone
Definition View.h:90
@ ViewAutoresizingFill
Definition View.h:93
@ ViewAutoresizingContain
Definition View.h:95
@ ViewAutoresizingWidth
Definition View.h:91
@ ViewAutoresizingHeight
Definition View.h:92
@ ViewAutoresizingFit
Definition View.h:94
@ ViewAlignmentBottomRight
Definition View.h:80
@ ViewAlignmentBottomLeft
Definition View.h:78
@ ViewAlignmentMiddleCenter
Definition View.h:76
@ ViewAlignmentInternal
Definition View.h:81
@ ViewAlignmentTopCenter
Definition View.h:73
@ ViewAlignmentTopRight
Definition View.h:74
@ ViewAlignmentMiddleRight
Definition View.h:77
@ ViewAlignmentTopLeft
Definition View.h:72
@ ViewAlignmentCenter
Definition View.h:70
@ ViewAlignmentLeft
Definition View.h:69
@ ViewAlignmentRight
Definition View.h:71
@ ViewAlignmentTop
Definition View.h:66
@ ViewAlignmentMiddleLeft
Definition View.h:75
@ ViewAlignmentBottom
Definition View.h:68
@ ViewAlignmentMiddle
Definition View.h:67
@ ViewAlignmentBottomCenter
Definition View.h:79
@ ViewAlignmentNone
Definition View.h:65
A ViewController manages a View and its descendants.
static Warning * initWithVaList(Warning *self, WarningType type, const char *fmt, va_list args)
Definition Warning.c:66
WarningType
Warning types.
Definition Warning.h:33
@ WarningTypeStyle
Definition Warning.h:34
@ WarningTypeLayout
Definition Warning.h:35
SDL_Rect MVC_TransformToWindow(SDL_Window *window, const SDL_Rect *rect)
Transforms the specified rectangle to normalized device coordinates in window.
Definition Window.c:29
static View * keyResponder(const WindowController *self)
static View * touchResponder(const WindowController *self)
A WindowController manages a ViewController and its descendants within an SDL_Window.
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:149
Outlets enable outbound data binding of Views through JSON.
Definition View+JSON.h:194
const char * identifier
The View identifier.
Definition View+JSON.h:199
SDL_Rect bounds
Definition View.c:968
bool hasOverflow
Definition View.c:969
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:75
Selectors are comprised of one or more SelectorSequences.
Definition Selector.h:49
The SimpleSelector type.
SimpleSelectorType type
The SimpleSelectorType.
char * pattern
The pattern, as provided by the user.
The Style type.
Definition Style.h:43
Dictionary * attributes
Definition Style.h:59
The Theme type.
Definition Theme.h:54
A constraint offered by an ancestor View to a descendant during View::sizeThatSatisfies.
Definition View.h:131
int value
The constraint value; a size in pixels.
Definition View.h:157
enum ViewConstraint::@3 mode
The horizontal inset of each of a View's corners.
Definition View.h:191
int bottomRight
Definition View.h:192
int bottomLeft
Definition View.h:192
int topRight
Definition View.h:192
int topLeft
Definition View.h:192
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:225
Stylesheet * stylesheet
An optional Stylesheet.
Definition View.h:443
bool clipsSubviews
If true, subviews will be clipped to this View's frame.
Definition View.h:309
View * superview
The super View.
Definition View.h:454
Array * subviews
The immediate subviews.
Definition View.h:448
ViewController * viewController
The ViewController.
Definition View.h:460
void invalidateStyle(View *self)
Invalidates the computed Style for this View and its descendants.
Definition View.c:1092
void setVisibility(View *self, ViewVisibility visibility)
Sets this View's visibility.
Definition View.c:1834
Style * style
The element-level Style of this View.
Definition View.h:425
ViewAlignment alignment
The alignment.
Definition View.h:242
SDL_Window * window
The window.
Definition View.h:480
bool needsApplyTheme
If true, this View will apply the Theme before it is drawn.
Definition View.h:353
View * viewWithResource(const Resource *resource, Outlet *outlets)
Instantiates a View initialized with the JSON data in resource.
Definition View.c:2142
ViewCornerCut cornerCut
The horizontal inset of each corner of the background and border.
Definition View.h:322
Array * visibleSubviews(const View *self)
Definition PageView.c:92
SDL_Color borderColor
The border color.
Definition View.h:275
View * viewWithResourceName(const char *name, Outlet *outlets)
Instantiates a View initialized with the JSON Resource with the specified name.
Definition View.c:2153
void applyStyle(View *self, const Style *style)
Applies the given Style to this View.
int autoresizingMask
The ViewAutoresizing bitmask.
Definition View.h:248
void detachStylesheet(View *self, SDL_Window *window)
Detaches this View's Stylesheet from the Theme associated with the given window.
Definition View.c:693
View * nextResponder
The next responder, or event handler, in the chain.
Definition View.h:392
ViewPointerEvents pointerEvents
Whether this View is the result of a hit test; its subviews always are.
Definition View.h:408
bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition View.h:377
char * identifier
An optional identifier.
Definition View.h:333
Array * warnings
The Warnings this View generated.
Definition View.h:474
ViewPadding padding
The padding.
Definition View.h:398
int borderWidth
The border width.
Definition View.h:287
SDL_Size minSize
The minimum size this View may be resized to during layout.
Definition View.h:345
int backgroundGradientAngle
The angle of the background gradient, in degrees, clockwise from south: 0 (the default) fills downwar...
Definition View.h:261
void updateBindings(View *self, ident data)
Updates data bindings, prompting the appropriate layout changes.
Style * computedStyle
The computed Style of this View.
Definition View.h:314
Set * classNames
The class names.
Definition View.h:293
ViewVisibility visibility
Whether this View is drawn, and contributes to its superview's size.
Definition View.h:468
int borderRadius
The corner radius applied to the background, border and bevel.
Definition View.h:281
SDL_Color backgroundGradientColor
The far color of the background gradient, which runs from View::backgroundColor.
Definition View.h:269
View * viewWithData(const Data *data, Outlet *outlets)
Instantiates a View initialized with the contents of data.
Definition View.c:2106
SDL_Size maxSize
The maximum size this View may be resized to during layout.
Definition View.h:339
View * init(View *self)
Initializes this View.
Definition Box.c:67
SDL_Color backgroundColor
The background color.
Definition View.h:254
bool stretch
Whether this View takes a share of the space its StackView leaves along its axis.
Definition View.h:436
SDL_Rect frame
The frame, relative to the superview.
Definition View.h:301
View * initWithFrame(View *self, const SDL_Rect *frame)
Initializes this View with the specified frame.
Definition View.c:1047
View * viewWithDictionary(const Dictionary *dictionary, Outlet *outlets)
Instantiates a View initialized with the attributes described in dictionary.
Definition View.c:2127
int top
Definition View.h:170
int bottom
Definition View.h:170
int right
Definition View.h:170
int left
Definition View.h:170
The Warning type.
Definition Warning.h:50
String * message
The message.
Definition Warning.h:71
WarningType type
The WarningType.
Definition Warning.h:66