ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
Control.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for OpenGL, SDL2 and GNU C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25
26#include "Control.h"
27
28const EnumName ControlBevelNames[] = MakeEnumNames(
29 MakeEnumAlias(ControlBevelNone, none),
30 MakeEnumAlias(ControlBevelInset, inset),
31 MakeEnumAlias(ControlBevelOutset, outset)
32);
33
34const EnumName ControlSelectionNames[] = MakeEnumNames(
35 MakeEnumAlias(ControlSelectionNone, none),
36 MakeEnumAlias(ControlSelectionSingle, single),
37 MakeEnumAlias(ControlSelectionMultiple, multiple)
38);
39
40const EnumName ControlStateNames[] = MakeEnumNames(
41 MakeEnumAlias(ControlStateDefault, default),
42 MakeEnumAlias(ControlStateHighlighted, highlighted),
43 MakeEnumAlias(ControlStateSelected, selected),
44 MakeEnumAlias(ControlStateFocused, focused)
45);
46
47#define _Class _Control
48
49#pragma mark - Object
50
54static void dealloc(Object *self) {
55
56 Control *this = (Control *) self;
57
58 release(this->actions);
59
60 super(Object, self, dealloc);
61}
62
63#pragma mark - View
64
68static _Bool acceptsFirstResponder(const View *self) {
69 return true;
70}
71
75static void applyStyle(View *self, const Style *style) {
76
77 super(View, self, applyStyle, style);
78
79 Control *this = (Control *) self;
80
81 const Inlet inlets[] = MakeInlets(
82 MakeInlet("bevel", InletTypeEnum, &this->bevel, (ident) (ControlBevelNames)),
83 MakeInlet("selection", InletTypeEnum, &this->selection, (ident) ControlSelectionNames)
84 );
85
86 $(self, bind, inlets, (Dictionary *) style->attributes);
87}
88
92static View *init(View *self) {
93 return (View *) $((Control *) self, initWithFrame, NULL);
94}
95
99static _Bool matchesSelector(const View *self, const SimpleSelector *simpleSelector) {
100
101 assert(simpleSelector);
102
103 const Control *this = (Control *) self;
104
105 switch (simpleSelector->type) {
107 if (strcmp("highlighted", simpleSelector->pattern) == 0) {
108 return $(this, isHighlighted);
109 } else if (strcmp("disabled", simpleSelector->pattern) == 0) {
110 return $(this, isDisabled);
111 } else if (strcmp("selected", simpleSelector->pattern) == 0) {
112 return $(this, isSelected);
113 } else if (strcmp("focused", simpleSelector->pattern) == 0) {
114 return $(this, isFocused);
115 } else if (strcmp("single", simpleSelector->pattern) == 0) {
116 return this->selection == ControlSelectionSingle;
117 } else if (strcmp("multiple", simpleSelector->pattern) == 0) {
118 return this->selection == ControlSelectionMultiple;
119 }
120 break;
121 default:
122 break;
123 }
124
125 return super(View, self, matchesSelector, simpleSelector);
126}
127
131static void render(View *self, Renderer *renderer) {
132
133 super(View, self, render, renderer);
134
135 Control *this = (Control *) self;
136
137 const SDL_Rect frame = $(self, renderFrame);
138
139 if (this->bevel == ControlBevelInset) {
140
141 $(renderer, setDrawColor, &Colors.Silver);
142
143 SDL_Point points[3];
144
145 points[0].x = frame.x;
146 points[0].y = frame.y + frame.h;
147
148 points[1].x = frame.x + frame.w;
149 points[1].y = frame.y + frame.h;
150
151 points[2].x = frame.x + frame.w;
152 points[2].y = frame.y;
153
154 $(renderer, drawLines, points, lengthof(points));
155
156 $(renderer, setDrawColor, &Colors.Black);
157
158 points[0].x = frame.x;
159 points[0].y = frame.y + frame.h;
160
161 points[1].x = frame.x;
162 points[1].y = frame.y;
163
164 points[2].x = frame.x + frame.w;
165 points[2].y = frame.y;
166
167 $(renderer, drawLines, points, lengthof(points));
168
169 } else if (this->bevel == ControlBevelOutset) {
170
171 $(renderer, setDrawColor, &Colors.Black);
172
173 SDL_Point points[3];
174
175 points[0].x = frame.x;
176 points[0].y = frame.y + frame.h;
177
178 points[1].x = frame.x + frame.w;
179 points[1].y = frame.y + frame.h;
180
181 points[2].x = frame.x + frame.w;
182 points[2].y = frame.y;
183
184 $(renderer, drawLines, points, lengthof(points));
185
186 $(renderer, setDrawColor, &Colors.Silver);
187
188 points[0].x = frame.x;
189 points[0].y = frame.y + frame.h;
190
191 points[1].x = frame.x;
192 points[1].y = frame.y;
193
194 points[2].x = frame.x + frame.w;
195 points[2].y = frame.y;
196
197 $(renderer, drawLines, points, lengthof(points));
198 }
199
200 if (this->state & ControlStateFocused) {
201
202 $(renderer, setDrawColor, &Colors.Charcoal);
203
204 $(renderer, drawRect, &frame);
205 }
206
207 $(renderer, setDrawColor, &Colors.White);
208}
209
213static void respondToEvent(View *self, const SDL_Event *event) {
214
215 Control *this = (Control *) self;
216
217 const ControlState state = this->state;
218
219 const _Bool didCaptureEvent = $(this, captureEvent, event);
220 if (didCaptureEvent) {
221
222 const Array *actions = (Array *) this->actions;
223 for (size_t i = 0; i < actions->count; i++) {
224
225 const Action *action = $(actions, objectAtIndex, i);
226 if (action->eventType == event->type) {
227 action->function(this, event, action->sender, action->data);
228 }
229 }
230 }
231
232 if (this->state != state) {
233 $(this, stateDidChange);
234 }
235
236 if (didCaptureEvent) {
237 return;
238 }
239
240 super(View, self, respondToEvent, event);
241}
242
243#pragma mark - Control
244
248static _Bool actionsForEvent_predicate(const ident obj, ident data) {
249 return ((Action *) obj)->eventType == ((SDL_Event *) data)->type;
250}
251
256static Array *actionsForEvent(const Control *self, const SDL_Event *event) {
257 return $((Array *) self->actions, filteredArray, actionsForEvent_predicate, (ident) event);
258}
259
264static void addActionForEventType(Control *self, SDL_EventType eventType, ActionFunction function, ident sender, ident data) {
265
266 Action *action = $(alloc(Action), initWithEventType, eventType, function, sender, data);
267
268 $(self->actions, addObject, action);
269
270 release(action);
271}
272
277static _Bool captureEvent(Control *self, const SDL_Event *event) {
278 return false;
279}
280
285static Control *initWithFrame(Control *self, const SDL_Rect *frame) {
286
287 self = (Control *) super(View, self, initWithFrame, frame);
288 if (self) {
289
290 self->actions = $$(MutableArray, arrayWithCapacity, 0);
291 assert(self->actions);
292 }
293
294 return self;
295}
296
301static _Bool isDisabled(const Control *self) {
303}
304
309static _Bool isFocused(const Control *self) {
311}
312
317static _Bool isHighlighted(const Control *self) {
319}
320
325static _Bool isSelected(const Control *self) {
327}
328
333static void stateDidChange(Control *self) {
334
335 View *this = (View *) self;
336
338 $(this, becomeFirstResponder);
339 } else {
340 $(this, resignFirstResponder);
341 }
342
343 $(this, invalidateStyle);
344
345 this->needsLayout = true;
346}
347
348#pragma mark - Class lifecycle
349
353static void initialize(Class *clazz) {
354
355 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
356
357 ((ViewInterface *) clazz->interface)->acceptsFirstResponder = acceptsFirstResponder;
358 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
359 ((ViewInterface *) clazz->interface)->init = init;
360 ((ViewInterface *) clazz->interface)->matchesSelector = matchesSelector;
361 ((ViewInterface *) clazz->interface)->render = render;
362 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
363
364 ((ControlInterface *) clazz->interface)->actionsForEvent = actionsForEvent;
365 ((ControlInterface *) clazz->interface)->addActionForEventType = addActionForEventType;
366 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
367 ((ControlInterface *) clazz->interface)->initWithFrame = initWithFrame;
368 ((ControlInterface *) clazz->interface)->isDisabled = isDisabled;
369 ((ControlInterface *) clazz->interface)->isFocused = isFocused;
370 ((ControlInterface *) clazz->interface)->isHighlighted = isHighlighted;
371 ((ControlInterface *) clazz->interface)->isSelected = isSelected;
372 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
373}
374
379Class *_Control(void) {
380 static Class *clazz;
381 static Once once;
382
383 do_once(&once, {
384 clazz = _initialize(&(const ClassDef) {
385 .name = "Control",
386 .superclass = _View(),
387 .instanceSize = sizeof(Control),
388 .interfaceOffset = offsetof(Control, interface),
389 .interfaceSize = sizeof(ControlInterface),
391 });
392 });
393
394 return clazz;
395}
396
397#undef _Class
void(* ActionFunction)(Control *control, const SDL_Event *event, ident sender, ident data)
The ActionFunction callback.
Definition: Action.h:43
const EnumName ControlBevelNames[]
Definition: Control.c:28
const EnumName ControlSelectionNames[]
Definition: Control.c:34
static _Bool actionsForEvent_predicate(const ident obj, ident data)
Predicate for actionsForEvent.
Definition: Control.c:248
const EnumName ControlStateNames[]
Definition: Control.c:40
static void dealloc(Object *self)
Definition: Control.c:54
static void initialize(Class *clazz)
Definition: Control.c:353
Controls are Views which capture events and dispatch Actions.
@ ControlBevelNone
Definition: Control.h:45
@ ControlBevelInset
Definition: Control.h:46
@ ControlBevelOutset
Definition: Control.h:47
@ ControlSelectionMultiple
Definition: Control.h:58
@ ControlSelectionSingle
Definition: Control.h:57
@ ControlSelectionNone
Definition: Control.h:56
ControlState
Control states, which are bit-masked.
Definition: Control.h:66
@ ControlStateDefault
Definition: Control.h:67
@ ControlStateFocused
Definition: Control.h:71
@ ControlStateHighlighted
Definition: Control.h:68
@ ControlStateDisabled
Definition: Control.h:69
@ ControlStateSelected
Definition: Control.h:70
@ SimpleSelectorTypePseudo
@ InletTypeEnum
Definition: View+JSON.h:75
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
Definition: View+JSON.h:221
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
Definition: View+JSON.h:216
Actions bind event-driven behavior to Controls.
Definition: Action.h:50
SDL_EventType eventType
The event type.
Definition: Action.h:71
ident data
The user data.
Definition: Action.h:66
Action * initWithEventType(Action *self, SDL_EventType eventType, ActionFunction function, ident sender, ident data)
Initializes this Action with the given function and data.
Definition: Action.c:36
ActionFunction function
The function.
Definition: Action.h:76
ident sender
The sender.
Definition: Action.h:81
Box * initWithFrame(Box *self, const SDL_Rect *frame)
Initializes this Box with the given frame.
Definition: Box.c:92
CollectionView * init(CollectionView *self, const SDL_Rect *frame)
Initializes this CollectionView with the specified frame and style.
W3C Color constants.
Definition: Colors.h:37
SDL_Color White
Definition: Colors.h:185
SDL_Color Charcoal
Definition: Colors.h:53
SDL_Color Black
Definition: Colors.h:46
SDL_Color Silver
Definition: Colors.h:169
Controls are Views which capture events and dispatch Actions.
Definition: Control.h:83
void stateDidChange(Control *self)
Called when the state of this Control changes.
Definition: Checkbox.c:103
_Bool captureEvent(Control *self, const SDL_Event *event)
Captures a given event, potentially altering the state of this Control.
Definition: Button.c:76
Array * actionsForEvent(const Control *self, const SDL_Event *event)
Definition: Control.c:256
Class * _Control(void)
The Control archetype.
Definition: Control.c:379
unsigned int state
The bit mask of ControlState.
Definition: Control.h:110
_Bool isSelected(const Control *self)
Definition: Control.c:325
void addActionForEventType(Control *self, SDL_EventType eventType, ActionFunction function, ident sender, ident data)
Adds an Action for the given event type to this Control.
Definition: Control.c:264
_Bool isFocused(const Control *self)
Definition: Control.c:309
_Bool isHighlighted(const Control *self)
Definition: Control.c:317
_Bool isDisabled(const Control *self)
Definition: Control.c:301
Inlets enable inbound data binding of View attributes through JSON.
Definition: View+JSON.h:155
The Renderer is responsible for rasterizing the View hierarchy of a WindowController.
Definition: Renderer.h:50
void drawRect(const Renderer *self, const SDL_Rect *rect)
Draws a rectangle using GL_LINE_LOOP.
Definition: Renderer.c:118
void drawLines(const Renderer *self, const SDL_Point *points, size_t count)
Draws line segments between adjacent points using GL_LINE_STRIP.
Definition: Renderer.c:105
void setDrawColor(Renderer *self, const SDL_Color *color)
Sets the primary color for drawing operations.
Definition: Renderer.c:281
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
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition: View.h:133
_Bool bind(View *self, const Inlet *inlets, const Dictionary *dictionary)
Performs data binding for the Inlets described in dictionary.
void invalidateStyle(View *self)
Invalidates the computed Style for this View and its descendants.
Definition: View.c:822
void becomeFirstResponder(View *self)
Become the first responder in the View hierarchy.
Definition: View.c:370
Class * _View(void)
The View archetype.
Definition: View.c:1769
void applyStyle(View *self, const Style *style)
Applies the given Style to this View.
_Bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
void respondToEvent(View *self, const SDL_Event *event)
Responds to the specified event.
Definition: Control.c:213
SDL_Rect renderFrame(const View *self)
Definition: View.c:1275
void resignFirstResponder(View *self)
Resigns first responder priority.
Definition: View.c:1315
_Bool acceptsFirstResponder(const View *self)
Definition: Control.c:68
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
Definition: Control.c:131