ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Control.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
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 MakeEnumAlias(ControlStateDisabled, disabled)
46);
47
48#define _Class _Control
49
50#pragma mark - Object
51
55static void dealloc(Object *self) {
56
57 Control *this = (Control *) self;
58
59 this->state = ControlStateDefault;
60
61 super(Object, self, dealloc);
62}
63
64#pragma mark - View
65
69static bool acceptsKeyResponder(const View *self) {
70
71 Control *this = (Control *) self;
72
73 return (this->state & ControlStateDisabled) == 0;
74}
75
79static bool acceptsTouchResponder(const View *self) {
80
81 Control *this = (Control *) self;
82
83 return (this->state & ControlStateDisabled) == 0;
84}
85
89static void applyStyle(View *self, const Style *style) {
90
91 super(View, self, applyStyle, style);
92
93 Control *this = (Control *) self;
94
95 const Inlet inlets[] = MakeInlets(
96 MakeInlet("bevel", InletTypeEnum, &this->bevel, (ident) ControlBevelNames),
97 MakeInlet("selection", InletTypeEnum, &this->selection, (ident) ControlSelectionNames)
98 );
99
100 $(self, bind, inlets, (Dictionary *) style->attributes);
101}
102
107static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
108
109 super(View, self, awakeWithDictionary, dictionary);
110
111 Control *this = (Control *) self;
112
113 const Inlet inlets[] = MakeInlets(
114 MakeInlet("state", InletTypeEnum, &this->state, (ident) ControlStateNames)
115 );
116
117 $(self, bind, inlets, dictionary);
118}
119
123static View *init(View *self) {
124 return (View *) $((Control *) self, initWithFrame, NULL);
125}
126
130static void becomeKeyResponder(View *self) {
131
132 Control *this = (Control *) self;
133
134 if (!$(this, isFocused)) {
135
136 this->state |= ControlStateFocused;
137
138 $(this, stateDidChange);
139 }
140
141 super(View, self, becomeKeyResponder);
142}
143
147static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector) {
148
149 assert(simpleSelector);
150
151 const Control *this = (Control *) self;
152
153 switch (simpleSelector->type) {
155 if (strcmp("highlighted", simpleSelector->pattern) == 0) {
156 return $(this, isHighlighted);
157 } else if (strcmp("disabled", simpleSelector->pattern) == 0) {
158 return $(this, isDisabled);
159 } else if (strcmp("selected", simpleSelector->pattern) == 0) {
160 return $(this, isSelected);
161 } else if (strcmp("focused", simpleSelector->pattern) == 0) {
162 return $(this, isFocused);
163 } else if (strcmp("single", simpleSelector->pattern) == 0) {
164 return this->selection == ControlSelectionSingle;
165 } else if (strcmp("multiple", simpleSelector->pattern) == 0) {
166 return this->selection == ControlSelectionMultiple;
167 }
168 break;
169 default:
170 break;
171 }
172
173 return super(View, self, matchesSelector, simpleSelector);
174}
175
179static void render(View *self, Renderer *renderer) {
180
181 super(View, self, render, renderer);
182
183 Control *this = (Control *) self;
184
185 const SDL_Rect frame = $(self, renderFrame);
186
187 if (this->bevel == ControlBevelInset) {
188
189 SDL_Point points[3];
190
191 points[0].x = frame.x;
192 points[0].y = frame.y + frame.h;
193
194 points[1].x = frame.x + frame.w;
195 points[1].y = frame.y + frame.h;
196
197 points[2].x = frame.x + frame.w;
198 points[2].y = frame.y;
199
200 $(renderer, drawLines, points, lengthof(points), &Colors.Silver);
201
202 points[0].x = frame.x;
203 points[0].y = frame.y + frame.h;
204
205 points[1].x = frame.x;
206 points[1].y = frame.y;
207
208 points[2].x = frame.x + frame.w;
209 points[2].y = frame.y;
210
211 $(renderer, drawLines, points, lengthof(points), &Colors.Black);
212
213 } else if (this->bevel == ControlBevelOutset) {
214
215 SDL_Point points[3];
216
217 points[0].x = frame.x;
218 points[0].y = frame.y + frame.h;
219
220 points[1].x = frame.x + frame.w;
221 points[1].y = frame.y + frame.h;
222
223 points[2].x = frame.x + frame.w;
224 points[2].y = frame.y;
225
226 $(renderer, drawLines, points, lengthof(points), &Colors.Black);
227
228 points[0].x = frame.x;
229 points[0].y = frame.y + frame.h;
230
231 points[1].x = frame.x;
232 points[1].y = frame.y;
233
234 points[2].x = frame.x + frame.w;
235 points[2].y = frame.y;
236
237 $(renderer, drawLines, points, lengthof(points), &Colors.Silver);
238 }
239
240 if (this->state & ControlStateFocused) {
241 $(renderer, drawRect, &frame, &Colors.Charcoal);
242 }
243}
244
248static void resignKeyResponder(View *self) {
249
250 Control *this = (Control *) self;
251
252 if ($(this, isHighlighted) || $(this, isFocused)) {
253
254 this->state &= ~ControlStateHighlighted;
255 this->state &= ~ControlStateFocused;
256
257 $(this, stateDidChange);
258 }
259
260 super(View, self, resignKeyResponder);
261}
262
266static void respondToEvent(View *self, const SDL_Event *event) {
267
268 Control *this = (Control *) self;
269
270 const ControlState state = (ControlState) this->state;
271 if (!(state & ControlStateDisabled)) {
272 if ($(this, captureEvent, event)) {
273 if (this->state != state) {
274 $(this, stateDidChange);
275 }
276 return;
277 }
278 }
279
280 super(View, self, respondToEvent, event);
281}
282
283#pragma mark - Control
284
289static bool captureEvent(Control *self, const SDL_Event *event) {
290 return false;
291}
292
297static Control *initWithFrame(Control *self, const SDL_Rect *frame) {
298
299 self = (Control *) super(View, self, initWithFrame, frame);
300 if (self) {
301
302 }
303
304 return self;
305}
306
311static bool isDisabled(const Control *self) {
313}
314
319static bool isFocused(const Control *self) {
321}
322
327static bool isHighlighted(const Control *self) {
329}
330
335static bool isSelected(const Control *self) {
337}
338
343static void stateDidChange(Control *self) {
344
345 View *this = (View *) self;
346
347 if (self->state & ControlStateFocused) {
348 $(this, emitViewEvent, ViewEventFocus, NULL);
349 } else {
350 $(this, emitViewEvent, ViewEventBlur, NULL);
351 }
352
353 $(this, invalidateStyle);
354
355 this->needsLayout = true;
356}
357
358#pragma mark - Class lifecycle
359
363static void initialize(Class *clazz) {
364
365 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
366
367 ((ViewInterface *) clazz->interface)->acceptsKeyResponder = acceptsKeyResponder;
368 ((ViewInterface *) clazz->interface)->acceptsTouchResponder = acceptsTouchResponder;
369 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
370 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
371 ((ViewInterface *) clazz->interface)->becomeKeyResponder = becomeKeyResponder;
372 ((ViewInterface *) clazz->interface)->init = init;
373 ((ViewInterface *) clazz->interface)->matchesSelector = matchesSelector;
374 ((ViewInterface *) clazz->interface)->render = render;
375 ((ViewInterface *) clazz->interface)->resignKeyResponder = resignKeyResponder;
376 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
377
378 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
379 ((ControlInterface *) clazz->interface)->initWithFrame = initWithFrame;
380 ((ControlInterface *) clazz->interface)->isDisabled = isDisabled;
381 ((ControlInterface *) clazz->interface)->isFocused = isFocused;
382 ((ControlInterface *) clazz->interface)->isHighlighted = isHighlighted;
383 ((ControlInterface *) clazz->interface)->isSelected = isSelected;
384 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
385}
386
391Class *_Control(void) {
392 static Class *clazz;
393 static Once once;
394
395 do_once(&once, {
396 clazz = _initialize(&(const ClassDef) {
397 .name = "Control",
398 .superclass = _View(),
399 .instanceSize = sizeof(Control),
400 .interfaceOffset = offsetof(Control, interface),
401 .interfaceSize = sizeof(ControlInterface),
403 });
404 });
405
406 return clazz;
407}
408
409#undef _Class
const EnumName ControlBevelNames[]
Definition Control.c:28
const EnumName ControlSelectionNames[]
Definition Control.c:34
static void becomeKeyResponder(View *self)
Definition Control.c:130
Class * _Control(void)
Definition Control.c:391
static void stateDidChange(Control *self)
Definition Control.c:343
static bool acceptsKeyResponder(const View *self)
Definition Control.c:69
static View * init(View *self)
Definition Control.c:123
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition Control.c:107
static Control * initWithFrame(Control *self, const SDL_Rect *frame)
Definition Control.c:297
const EnumName ControlStateNames[]
Definition Control.c:40
static void respondToEvent(View *self, const SDL_Event *event)
Definition Control.c:266
static void applyStyle(View *self, const Style *style)
Definition Control.c:89
static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
Definition Control.c:147
static bool captureEvent(Control *self, const SDL_Event *event)
Definition Control.c:289
static bool isHighlighted(const Control *self)
Definition Control.c:327
static void render(View *self, Renderer *renderer)
Definition Control.c:179
static void dealloc(Object *self)
Definition Control.c:55
static void resignKeyResponder(View *self)
Definition Control.c:248
static bool isDisabled(const Control *self)
Definition Control.c:311
static void initialize(Class *clazz)
Definition Control.c:363
static bool acceptsTouchResponder(const View *self)
Definition Control.c:79
static bool isFocused(const Control *self)
Definition Control.c:319
static bool isSelected(const Control *self)
Definition Control.c:335
Controls are Views which capture and respond to events.
@ ControlBevelNone
Definition Control.h:44
@ ControlBevelInset
Definition Control.h:45
@ ControlBevelOutset
Definition Control.h:46
@ ControlSelectionMultiple
Definition Control.h:57
@ ControlSelectionSingle
Definition Control.h:56
@ ControlSelectionNone
Definition Control.h:55
ControlState
Control states, which are bit-masked.
Definition Control.h:65
@ ControlStateDefault
Definition Control.h:66
@ ControlStateFocused
Definition Control.h:69
@ ControlStateHighlighted
Definition Control.h:67
@ ControlStateDisabled
Definition Control.h:70
@ ControlStateSelected
Definition Control.h:68
static void drawLines(const Renderer *self, const SDL_Point *points, size_t count, const SDL_Color *color)
Definition Renderer.c:125
static void drawRect(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:169
@ SimpleSelectorTypePseudo
@ ViewEventFocus
A Control's state has focus.
Definition Types.h:120
@ ViewEventBlur
A Control has lost focus.
Definition Types.h:105
@ 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
Class * _View(void)
Definition View.c:2067
static void invalidateStyle(View *self)
Definition View.c:1033
static SDL_Rect renderFrame(const View *self)
Definition View.c:1455
static void emitViewEvent(View *self, ViewEvent code, ident data)
Definition View.c:736
W3C Color constants.
Definition Colors.h:37
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 and respond to events.
Definition Control.h:83
unsigned int state
The bit mask of ControlState.
Definition Control.h:104
bool isDisabled(const Control *self)
Definition Control.c:311
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:155
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:70
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:134
bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)