27#include <Objectively.h>
65#pragma mark - ObjectInterface
74 free(this->identifier);
76 release(this->classNames);
77 release(this->computedStyle);
79 release(this->stylesheet);
80 release(this->subviews);
81 release(this->warnings);
83 this->superview = NULL;
95 String *classNames = $((Object *) this->classNames,
description);
96 String *
description = str(
"%s@%p %s [%d, %d, %d, %d]",
100 this->frame.x, this->frame.y, this->frame.w, this->frame.h);
124 String *
string = $$(String, stringWithCharacters, className);
149 assert(subview != other);
157 const Array *subviews = (Array *) self->
subviews;
158 const ssize_t index = $(subviews, indexOfObject, other);
161 if (index == (ssize_t) (subviews->count - 1)) {
162 $(self->
subviews, addObject, subview);
164 $(self->
subviews, insertObjectAtIndex, subview, index + 1);
167 $(self->
subviews, insertObjectAtIndex, subview, index);
170 $(self->
subviews, addObject, subview);
195 if (strcmp(identifier, view->
identifier) == 0) {
251 assert(computedStyle);
262 release(computedStyle);
304 Data *data = $$(Data, dataWithConstMemory, (uint8_t *) chars, strlen(chars));
317 Dictionary *dictionary = $$(JSONSerialization, objectFromData, data, 0);
339 $(self,
bind, inlets, dictionary);
359 Resource *resource = $$(Resource, resourceWithName, name);
378static _Bool
_bind(
View *self,
const Inlet *inlets,
const Dictionary *dictionary) {
419 if (last != subview) {
446 if (SDL_IntersectRect(&
clippingFrame, &frame, &frame) ==
false) {
450 String *superdesc = $((Object *) superview,
description);
452 MVC_LogVerbose(
"%s is clipped by %s\n", desc->chars, superdesc->chars);
458 frame.w = frame.h = 0;
476 return (_Bool) !!SDL_PointInRect(point, &frame);
496 if (strcmp(identifier, self->
identifier) == 0) {
497 return (
View *) self;
501 const Array *subviews = (Array *) self->
subviews;
502 for (
size_t i = 0; i < subviews->count; i++) {
503 const View *subview = $(subviews, objectAtIndex, i);
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);
576 if (self->
hidden ==
false) {
592 enumerator(self, data);
607 const ssize_t index = $(siblings, indexOfObject, (
const ident) self);
609 enumerator($(siblings, objectAtIndex, index - 1), data);
611 if (index < (ssize_t) (siblings->count - 1)) {
612 enumerator($(siblings, objectAtIndex, index + 1), data);
626 enumerator(view, data);
651 const Array *subviews = (Array *) self->
subviews;
652 for (
size_t i = 0; i < subviews->count; i++) {
654 View *subview = $(subviews, objectAtIndex, i);
655 enumerator(subview, data);
672 for (
size_t i = 0; i < siblings->count; i++) {
673 View *sibling = $(siblings, objectAtIndex, i);
674 if (sibling != self) {
675 enumerator(sibling, data);
689 const Array *subviews = (Array *) self->
subviews;
690 for (
size_t i = 0; i < subviews->count; i++) {
691 enumerator((
View *) subviews->elements[i], data);
716 return SDL_GetWindowData(window,
"firstResponder");
723 return strcmp(((String *) obj)->chars, (
const char *) data) == 0;
745 if (self->
hidden ==
false) {
749 const Array *subviews = (Array *) self->
subviews;
750 for (
size_t i = subviews->count; i; i--) {
752 const View *subview = $(subviews, objectAtIndex, i - 1);
755 return (
View *) view;
759 return (
View *) self;
780 self = (
View *) super(Object, self,
init);
784 self->
frame = *frame;
787 self->
classNames = $$(MutableSet, setWithCapacity, 0);
793 self->
subviews = $$(MutableArray, arrayWithCapacity, 0);
799 self->
warnings = $$(MutableArray, arrayWithCapacity, 0);
871 for (
const View *view = self; view; view = view->
superview) {
907 String *that = $((Object *) self->superview,
description);
909 MVC_LogDebug(
"%s exceeds superview bounds %s\n", this->chars, that->chars);
911 $(self,
warn,
"%s exceeds superview bounds %s\n", this->chars, that->chars);
951 const Array *subviews = (Array *) self->
subviews;
952 for (
size_t i = 0; i < subviews->count; i++) {
954 View *subview = (
View *) $(subviews, objectAtIndex, i);
971 $(subview,
resize, &subviewSize);
975 subview->
frame.x = 0;
987 subview->
frame.y = 0;
1005 assert(simpleSelector);
1007 const char *pattern = simpleSelector->
pattern;
1009 switch (simpleSelector->
type) {
1017 const Class *clazz = classForName(pattern);
1019 return $((Object *) self, isKindOfClass, clazz);
1029 return strcmp(self->
identifier, pattern) == 0;
1035 if (strcmp(
"first-child", pattern) == 0) {
1039 }
else if (strcmp(
"last-child", pattern) == 0) {
1043 }
else if (strcmp(
"nth-child(even)", pattern) == 0) {
1045 return ($((Array *) self->
superview->
subviews, indexOfObject, (ident) self) & 1) == 0;
1047 }
else if (strcmp(
"nth-child(odd)", pattern) == 0) {
1051 }
else if (strcmp(
"hover", pattern) == 0) {
1053 SDL_GetMouseState(&point.x, &point.y);
1075 if (self->
window != window) {
1093 MutableArray *parts = $$(MutableArray, array);
1095 const View *view = self;
1098 Array *classNames = $((Set *) view->
classNames, allObjects);
1103 }
else if (classNames->count) {
1104 part = str(
".%s", ((String *) $(classNames, firstObject))->chars);
1106 part = str(
"%s", classnameof(view));
1109 release(classNames);
1111 $(parts, insertObjectAtIndex, part, 0);
1121 String *
path = $((Array *) parts, componentsJoinedByCharacters,
" > ");
1161 String *
string = $$(String, stringWithCharacters, className);
1196 $(self->
subviews, removeObject, subview);
1211 SDL_TriggerBreakpoint();
1277 SDL_Rect frame = self->
frame;
1279 const View *view = self;
1283 frame.x += superview->
frame.x;
1284 frame.y += superview->
frame.y;
1305 assert(replacement);
1350 assert(*outlet->view);
1383 Set *selection = $(selector,
select, self);
1398 assert(view->
window == window);
1399 SDL_SetWindowData(window,
"firstResponder", view);
1406 SDL_SetWindowData(window,
"firstResponder", NULL);
1451 for (
size_t i = 0; i < subviews->count; i++) {
1453 const View *subview = $(subviews, objectAtIndex, i);
1456 SDL_Point subviewOrigin =
MakePoint(0, 0);
1465 size.
w = max(
size.
w, subviewOrigin.x + subviewSize.
w);
1466 size.
h = max(
size.
h, subviewOrigin.y + subviewSize.
h);
1511 const Array *subviews = (Array *) self->
subviews;
1512 for (
size_t i = 0; i < subviews->count; i++) {
1514 View *subview = $(subviews, objectAtIndex, i);
1517 if (strcmp(identifier, subview->
identifier) == 0) {
1560 Data *data = $$(Data, dataWithConstMemory, (ident) chars, strlen(chars));
1575 Dictionary *dictionary = $$(JSONSerialization, objectFromData, data, 0);
1579 release(dictionary);
1616 Resource *resource = $$(Resource, resourceWithName, name);
1649 va_start(args, fmt);
1651 String *warning = $(alloc(String), initWithVaList, fmt, args);
1656 $(self->
warnings, addObject, warning);
1672#pragma mark - View class methods
1679 ((ObjectInterface *) clazz->interface)->dealloc =
dealloc;
1680 ((ObjectInterface *) clazz->interface)->description =
description;
1683 ((ViewInterface *) clazz->interface)->addClassName =
addClassName;
1684 ((ViewInterface *) clazz->interface)->addSubview =
addSubview;
1687 ((ViewInterface *) clazz->interface)->applyStyle =
applyStyle;
1688 ((ViewInterface *) clazz->interface)->applyTheme =
applyTheme;
1692 ((ViewInterface *) clazz->interface)->awakeWithData =
awakeWithData;
1697 ((ViewInterface *) clazz->interface)->bind =
_bind;
1698 ((ViewInterface *) clazz->interface)->bounds =
bounds;
1700 ((ViewInterface *) clazz->interface)->clippingFrame =
clippingFrame;
1701 ((ViewInterface *) clazz->interface)->containsPoint =
containsPoint;
1702 ((ViewInterface *) clazz->interface)->depth =
depth;
1705 ((ViewInterface *) clazz->interface)->didMoveToWindow =
didMoveToWindow;
1706 ((ViewInterface *) clazz->interface)->didReceiveEvent =
didReceiveEvent;
1707 ((ViewInterface *) clazz->interface)->draw =
draw;
1708 ((ViewInterface *) clazz->interface)->enumerate =
enumerate;
1716 ((ViewInterface *) clazz->interface)->firstResponder =
firstResponder;
1718 ((ViewInterface *) clazz->interface)->hitTest =
hitTest;
1719 ((ViewInterface *) clazz->interface)->
init =
init;
1722 ((ViewInterface *) clazz->interface)->isContainer =
isContainer;
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;
1733 ((ViewInterface *) clazz->interface)->removeClassName =
removeClassName;
1735 ((ViewInterface *) clazz->interface)->removeSubview =
removeSubview;
1736 ((ViewInterface *) clazz->interface)->render =
render;
1739 ((ViewInterface *) clazz->interface)->renderFrame =
renderFrame;
1740 ((ViewInterface *) clazz->interface)->replaceSubview =
replaceSubview;
1742 ((ViewInterface *) clazz->interface)->resize =
resize;
1743 ((ViewInterface *) clazz->interface)->resolve =
resolve;
1744 ((ViewInterface *) clazz->interface)->respondToEvent =
respondToEvent;
1745 ((ViewInterface *) clazz->interface)->select =
_select;
1747 ((ViewInterface *) clazz->interface)->size =
size;
1749 ((ViewInterface *) clazz->interface)->sizeThatFits =
sizeThatFits;
1750 ((ViewInterface *) clazz->interface)->sizeToContain =
sizeToContain;
1751 ((ViewInterface *) clazz->interface)->sizeToFit =
sizeToFit;
1753 ((ViewInterface *) clazz->interface)->updateBindings =
updateBindings;
1754 ((ViewInterface *) clazz->interface)->viewport =
viewport;
1761 ((ViewInterface *) clazz->interface)->warn =
warn;
1770 static Class *clazz;
1774 clazz = _initialize(&(
const ClassDef) {
1776 .superclass = _Object(),
1777 .instanceSize =
sizeof(
View),
1778 .interfaceOffset = offsetof(
View, interface),
1779 .interfaceSize =
sizeof(ViewInterface),
View logging facilities via SDL_Log.
#define MVC_LogVerbose(fmt,...)
#define MVC_LogDebug(fmt,...)
#define MVC_LogEnabled(priority)
@ SimpleSelectorTypePseudo
@ SimpleSelectorTypeClass
@ SimpleSelectorTypeUniversal
#define MakeSize(w, h)
Creates an SDL_Size with the given dimensions.
void(* ViewEnumerator)(View *view, ident data)
A function type for View enumeration.
#define MakePoint(x, y)
Creates an SDL_Point with the given coordinates.
_Bool bindInlets(const Inlet *inlets, const Dictionary *dictionary)
Binds each Inlet specified in inlets to the data provided in dictionary.
#define BindInlet(inlet, obj)
Binds the Inlet to obj by invoking the appropriate InletBinding function.
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
const EnumName ViewAlignmentNames[]
static _Bool visibleSubviews_filter(ident obj, ident data)
Predicate for visibleSubviews.
static Set * _select(View *self, const char *rule)
static void removeAllSubviews_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for removeAllSubviews.
static void renderDeviceWillReset_recurse(View *subview, ident data)
ViewEnumerator for renderDeviceWillReset recursion.
static _Bool hasClassName_predicate(const ident obj, ident data)
Predicate for hasClassName.
static String * description(const Object *self)
static void layoutIfNeeded_recurse(View *subview, ident data)
ViewEnumerator for layoutIfNeeded recursion.
static _Bool _bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
static void dealloc(Object *self)
static void moveToWindow_recurse(View *subview, ident data)
ViewEnumerator for moveToWindow recursion.
static void invalidateStyle_enumerate(View *view, ident data)
ViewEnumerator for invalidateStyle.
static void updateBindings_recurse(View *subview, ident data)
ViewEnumerator for updateBindings recursion.
static void initialize(Class *clazz)
const EnumName ViewAutoresizingNames[]
static void renderDeviceDidReset_recurse(View *subview, ident data)
ViewEnumerator for renderDeviceDidReset recursion.
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
#define ViewAlignmentMaskBottom
#define ViewAlignmentMaskTop
#define ViewAlignmentMaskMiddle
#define ViewAlignmentMaskHorizontal
ViewPosition
Relative positioning of subviews within their superview.
#define ViewAlignmentMaskVertical
@ ViewAutoresizingContain
@ ViewAlignmentBottomRight
@ ViewAlignmentBottomLeft
@ ViewAlignmentMiddleCenter
@ ViewAlignmentMiddleRight
@ ViewAlignmentMiddleLeft
@ ViewAlignmentBottomCenter
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.
Box * initWithFrame(Box *self, const SDL_Rect *frame)
Initializes this Box with the given frame.
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.
void renderDeviceDidReset(Font *self)
This method should be invoked when the render context is invalidated.
SDL_Size size(const Image *self)
Inlets enable inbound data binding of View attributes through JSON.
Outlets enable outbound data binding of Views through JSON.
const char * identifier
The View identifier.
The Renderer is responsible for rasterizing the View hierarchy of a WindowController.
void drawRectFilled(const Renderer *self, const SDL_Rect *rect)
Fills a rectangle using glRecti.
void drawRect(const Renderer *self, const SDL_Rect *rect)
Draws a rectangle using GL_LINE_LOOP.
void renderDeviceWillReset(Renderer *self)
This method is invoked when the render device will become reset.
void drawView(Renderer *self, View *view)
Draws the given View, setting the clipping frame and invoking View::render.
void setDrawColor(Renderer *self, const SDL_Color *color)
Sets the primary color for drawing operations.
Selectors are comprised of one or more SelectorSequences.
Selector * initWithRule(Selector *self, const char *rule)
Initializes this Selector with the given rule.
void enumerateSelection(const Selector *self, View *view, ViewEnumerator enumerator, ident data)
Selects from view and applies the given ViewEnumerator to all matched Views.
Array * select(const Selector *self, View *view)
SimpleSelectorType type
The SimpleSelectorType.
char * pattern
The pattern, as provided by the user.
Style * initWithAttributes(Style *self, const Dictionary *attributes)
Initializes this Style with the given attributes.
_Bool isComputedEqual(const Style *self, const Style *other)
Performs a fast, rule-based comparison of this Style to the given Style.
void addAttributes(Style *self, const Dictionary *attributes)
Adds or replaces the attribtues in attributes to this Style.
Style * computeStyle(const Theme *self, View *view)
void removeStylesheet(Theme *self, Stylesheet *stylesheet)
Removes the given Stylesheet from this Theme.
void addStylesheet(Theme *self, Stylesheet *stylesheet)
Adds the specified Stylesheet to this Theme.
Theme * theme(SDL_Window *window)
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Stylesheet * stylesheet
An optional Stylesheet.
String * path(const View *self)
_Bool hasClassName(const View *self, cosnt char *className)
_Bool isVisible(const View *self)
void resolve(View *self, Outlet *outlets)
Resolves the given Outlets from this View's hierarchy.
MutableArray * subviews
The immediate subviews.
descendantWithIdentifier(const View *self, const char *identifier)
void updateBindings(View *self)
Updates data bindings, prompting the appropriate layout changes.
View * superview
The super View.
SDL_Rect bounds(const View *self)
MutableArray * warnings
The warnings this View generated.
_Bool bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
Performs data binding for the Inlets described in dictionary.
int depth(const View *self)
void awakeWithData(View *self, const Data *data)
Wakes this View with the specified JSON Data.
void applyThemeIfNeeded(View *self, const Theme *theme)
Recursively applies the Theme to this View and its subviews.
ViewController * viewController
The ViewController.
SDL_Rect clippingFrame(const View *self)
void invalidateStyle(View *self)
Invalidates the computed Style for this View and its descendants.
void becomeFirstResponder(View *self)
Become the first responder in the View hierarchy.
_Bool clipsSubviews
If true, subviews will be clipped to this View's frame.
Style * style
The element-level Style of this View.
void awakeWithResource(View *self, const Resource *resource)
Wakes this View with the specified Resource.
ViewAlignment alignment
The alignment.
_Bool needsLayout
If true, this View will layout its subviews before it is drawn.
View * hitTest(const View *self, const SDL_Point *point)
Performs a hit test against this View and its descendants for the given point.
void enumerateSuperview(const View *self, ViewEnumerator enumerator, ident data)
Enumerates the superview of this View, if any, applying enumerator to it.
_Bool needsApplyTheme
If true, this View will apply the Theme before it is drawn.
SDL_Window * window
The window.
View * viewWithCharacters(const char *chars, Outlet *outlets)
Instantiates a View initialized with the given null-terminated JSON C string.
_Bool hidden
If true, this View is not drawn.
Class * _View(void)
The View archetype.
View * viewWithResource(const Resource *resource, Outlet *outlets)
Instantiates a View initialized with the JSON data in resource.
Array * visibleSubviews(const View *self)
View * firstResponder(SDL_Window *window)
SDL_Color borderColor
The border color.
void addSubview(View *self, View *subview)
Adds a subview to this view, to be drawn above its siblings.
void addClassName(View *self, const char *className)
Adds the given class name to this View.
View * viewWithResourceName(const char *name, Outlet *outlets)
Instantiates a View initialized with the JSON Resource with the specified name.
_Bool isFirstResponder(const View *self)
void applyStyle(View *self, const Style *style)
Applies the given Style to this View.
subviewWithIdentifier(const View *self, const char *identifier)
void warn(View *self, const char *fmt,...)
Appends a warning for this View.
void resize(View *self, const SDL_Size *size)
Resizes this View to the specified size.
void awakeWithResourceName(View *self, const char *name)
Wakes this View with the Resource by the specified name.
void enumerateAncestors(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all ancestors of this View, applying enumerator to each.
_Bool didReceiveEvent(const View *self, const SDL_Event *event)
int autoresizingMask
The ViewAutoresizing bitmask.
void awakeWithCharacters(View *self, const char *chars)
Wakes this View with the given null-terminated JSON C string.
void setFirstResponder(SDL_Window *window, View *view)
Sets the first responder for the given window.
void detachStylesheet(View *self, SDL_Window *window)
Detaches this View's Stylesheet from the Theme associated with the given window.
void removeFromSuperview(View *self)
Removes this View from its superview.
View * nextResponder
The next responder, or event handler, in the chain.
SDL_Rect viewport(const View *self)
void removeAllClassNames(View *self)
Removes all class names from this View.
_Bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
char * identifier
An optional identifier.
void sizeThatFits(const View *self)
void * draw(View *self, Renderer *renderer)
Draws this View.
void respondToEvent(View *self, const SDL_Event *event)
Responds to the specified event.
void removeAllSubviews(View *self)
Removes all subviews from this View.
void enumerateSiblings(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all siblings of this View, applying enumerator to each.
ViewPadding padding
The padding.
void bringSubviewToFront(View *self, View *subview)
Brings the specified subview to the front.
void replaceSubview(View *self, View *subview, View *replacement)
Replaces the specified subview with the given replacement.
_Bool containsPoint(const View *self, const SDL_Point *point)
void awakeWithDictionary(View *self, const Dictionary *dictionary)
Wakes this View with the specified Dictionary.
int borderWidth
The border width.
SDL_Size minSize
The minimum size this View may be resized to during layout.
void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all descendants of this View, applying enumerator to each.
void enumerateAdjacent(const View *self, ViewEnumerator enumerator, ident data)
Enumerates adjacent siblings of this View, applying enumerator to each.
SDL_Rect renderFrame(const View *self)
void resignFirstResponder(View *self)
Resigns first responder priority.
_Bool isDescendantOfView(const View *self, const View *view)
void didMoveToWindow(View *self, SDL_Window *window)
Informs this View that it has been added to the View hierachy of the given window.
Style * computedStyle
The computed Style of this View.
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
SDL_Size sizeThatContains(const View *self)
void addSubviewRelativeTo(View *self, View *subview, View *other, ViewPosition position)
Adds a subview to this view, positioned relatively to other.
View * viewWithData(const Data *data, Outlet *outlets)
Instantiates a View initialized with the contents of data.
void enumerate(const View *self, ViewEnumerator enumerator, ident data)
MutableSet * classNames
The class names.
SDL_Size maxSize
The maximum size this View may be resized to during layout.
_Bool isContainer(const View *self)
View * init(View *self)
Initializes this View.
ancestorWithIdentifier(const View *self, const char *identifier)
void layoutIfNeeded(View *self)
Recursively updates the layout of this View and its subviews.
SDL_Color backgroundColor
The background color.
void applyTheme(View *self, const Theme *theme)
Applies the given Theme to this View.
void attachStylesheet(View *self, SDL_Window *window)
Attaches this View's Stylesheet to the Theme associated with the given window.
void moveToWindow(View *self, SDL_Window *window)
Moves this View to the View hierarchy of the given window.
SDL_Rect frame
The frame, relative to the superview.
void sizeToContain(View *self)
Resizes this View to contain its subviews.
void willMoveToWindow(View *self, SDL_Window *window)
Informs this View that it will be added to the View hierarchy for the given window.
_Bool acceptsFirstResponder(const View *self)
void enumerateSubviews(const View *self, ViewEnumerator enumerator, ident data)
Enumerates all subviews of this View, applying enumerator to each.
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
void sizeToFit(View *self)
Resizes this View to fit its subviews.
void removeClassName(View *self, const char *className)
Removes the given class name to this View.
void removeSubview(View *self, View *subview)
Removes the given subview from this View.
View * viewWithDictionary(const Dictionary *dictionary, Outlet *outlets)
Instantiates a View initialized with the attributes described in dictionary.