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("bevel-dark-color", InletTypeColor, &this->bevelDarkColor, NULL),
98 MakeInlet("bevel-light-color", InletTypeColor, &this->bevelLightColor, NULL),
99 MakeInlet("selection", InletTypeEnum, &this->selection, (ident) ControlSelectionNames)
100 );
101
102 $(self, bind, inlets, (Dictionary *) style->attributes);
103}
104
109static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
110
111 super(View, self, awakeWithDictionary, dictionary);
112
113 Control *this = (Control *) self;
114
115 const Inlet inlets[] = MakeInlets(
116 MakeInlet("state", InletTypeEnum, &this->state, (ident) ControlStateNames)
117 );
118
119 $(self, bind, inlets, dictionary);
120}
121
125static View *init(View *self) {
126 return (View *) $((Control *) self, initWithFrame, NULL);
127}
128
132static void becomeKeyResponder(View *self) {
133
134 Control *this = (Control *) self;
135
136 if (!$(this, isFocused)) {
137
138 this->state |= ControlStateFocused;
139
140 $(this, stateDidChange);
141 }
142
143 super(View, self, becomeKeyResponder);
144}
145
149static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector) {
150
151 assert(simpleSelector);
152
153 const Control *this = (Control *) self;
154
155 switch (simpleSelector->type) {
157 if (strcmp("highlighted", simpleSelector->pattern) == 0) {
158 return $(this, isHighlighted);
159 } else if (strcmp("disabled", simpleSelector->pattern) == 0) {
160 return $(this, isDisabled);
161 } else if (strcmp("selected", simpleSelector->pattern) == 0) {
162 return $(this, isSelected);
163 } else if (strcmp("focused", simpleSelector->pattern) == 0) {
164 return $(this, isFocused);
165 } else if (strcmp("single", simpleSelector->pattern) == 0) {
166 return this->selection == ControlSelectionSingle;
167 } else if (strcmp("multiple", simpleSelector->pattern) == 0) {
168 return this->selection == ControlSelectionMultiple;
169 }
170 break;
171 default:
172 break;
173 }
174
175 return super(View, self, matchesSelector, simpleSelector);
176}
177
181static void render(View *self, Renderer *renderer) {
182
183 super(View, self, render, renderer);
184
185 Control *this = (Control *) self;
186
187 const SDL_Rect frame = $(self, renderFrame);
188
189 if (self->borderRadius > 0) {
190
191 if (this->bevel == ControlBevelInset) {
192 $(renderer, drawBevel, &frame, self->borderRadius, 1, &this->bevelDarkColor, &this->bevelLightColor);
193 } else if (this->bevel == ControlBevelOutset) {
194 $(renderer, drawBevel, &frame, self->borderRadius, 1, &this->bevelLightColor, &this->bevelDarkColor);
195 }
196
197 if (this->state & ControlStateFocused) {
198 $(renderer, drawRoundedRect, &frame, self->borderRadius, 1, &Colors.Charcoal);
199 }
200
201 } else {
202
203 if (this->bevel == ControlBevelInset) {
204
205 SDL_Point points[3];
206
207 points[0].x = frame.x;
208 points[0].y = frame.y + frame.h;
209
210 points[1].x = frame.x + frame.w;
211 points[1].y = frame.y + frame.h;
212
213 points[2].x = frame.x + frame.w;
214 points[2].y = frame.y;
215
216 $(renderer, drawLines, points, lengthof(points), &this->bevelLightColor);
217
218 points[0].x = frame.x;
219 points[0].y = frame.y + frame.h;
220
221 points[1].x = frame.x;
222 points[1].y = frame.y;
223
224 points[2].x = frame.x + frame.w;
225 points[2].y = frame.y;
226
227 $(renderer, drawLines, points, lengthof(points), &this->bevelDarkColor);
228
229 } else if (this->bevel == ControlBevelOutset) {
230
231 SDL_Point points[3];
232
233 points[0].x = frame.x;
234 points[0].y = frame.y + frame.h;
235
236 points[1].x = frame.x + frame.w;
237 points[1].y = frame.y + frame.h;
238
239 points[2].x = frame.x + frame.w;
240 points[2].y = frame.y;
241
242 $(renderer, drawLines, points, lengthof(points), &this->bevelDarkColor);
243
244 points[0].x = frame.x;
245 points[0].y = frame.y + frame.h;
246
247 points[1].x = frame.x;
248 points[1].y = frame.y;
249
250 points[2].x = frame.x + frame.w;
251 points[2].y = frame.y;
252
253 $(renderer, drawLines, points, lengthof(points), &this->bevelLightColor);
254 }
255
256 if (this->state & ControlStateFocused) {
257 $(renderer, drawRect, &frame, &Colors.Charcoal);
258 }
259 }
260}
261
265static void resignKeyResponder(View *self) {
266
267 Control *this = (Control *) self;
268
269 if ($(this, isHighlighted) || $(this, isFocused)) {
270
271 this->state &= ~ControlStateHighlighted;
272 this->state &= ~ControlStateFocused;
273
274 $(this, stateDidChange);
275 }
276
277 super(View, self, resignKeyResponder);
278}
279
283static void respondToEvent(View *self, const SDL_Event *event) {
284
285 Control *this = (Control *) self;
286
287 retain(self);
288
289 const ControlState state = (ControlState) this->state;
290 if (!(state & ControlStateDisabled)) {
291 if ($(this, captureEvent, event)) {
292 if (this->state != state) {
293 $(this, stateDidChange);
294 }
295 release(self);
296 return;
297 }
298 }
299
300 super(View, self, respondToEvent, event);
301
302 release(self);
303}
304
305#pragma mark - Control
306
311static bool captureEvent(Control *self, const SDL_Event *event) {
312 return false;
313}
314
319static Control *initWithFrame(Control *self, const SDL_Rect *frame) {
320
321 self = (Control *) super(View, self, initWithFrame, frame);
322 if (self) {
325 }
326
327 return self;
328}
329
334static bool isDisabled(const Control *self) {
336}
337
342static bool isFocused(const Control *self) {
344}
345
350static bool isHighlighted(const Control *self) {
352}
353
358static bool isSelected(const Control *self) {
360}
361
366static void stateDidChange(Control *self) {
367
368 View *this = (View *) self;
369
370 if (self->state & ControlStateFocused) {
371 $(this, emitViewEvent, ViewEventFocus, NULL);
372 } else {
373 $(this, emitViewEvent, ViewEventBlur, NULL);
374 }
375
376 $(this, invalidateStyle);
377
378 $(this, setNeedsLayout);
379}
380
381#pragma mark - Class lifecycle
382
386static void initialize(Class *clazz) {
387
388 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
389
390 ((ViewInterface *) clazz->interface)->acceptsKeyResponder = acceptsKeyResponder;
391 ((ViewInterface *) clazz->interface)->acceptsTouchResponder = acceptsTouchResponder;
392 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
393 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
394 ((ViewInterface *) clazz->interface)->becomeKeyResponder = becomeKeyResponder;
395 ((ViewInterface *) clazz->interface)->init = init;
396 ((ViewInterface *) clazz->interface)->matchesSelector = matchesSelector;
397 ((ViewInterface *) clazz->interface)->render = render;
398 ((ViewInterface *) clazz->interface)->resignKeyResponder = resignKeyResponder;
399 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
400
401 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
402 ((ControlInterface *) clazz->interface)->initWithFrame = initWithFrame;
403 ((ControlInterface *) clazz->interface)->isDisabled = isDisabled;
404 ((ControlInterface *) clazz->interface)->isFocused = isFocused;
405 ((ControlInterface *) clazz->interface)->isHighlighted = isHighlighted;
406 ((ControlInterface *) clazz->interface)->isSelected = isSelected;
407 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
408}
409
414Class *_Control(void) {
415 static Class *clazz;
416 static Once once;
417
418 do_once(&once, {
419 clazz = _initialize(&(const ClassDef) {
420 .name = "Control",
421 .superclass = _View(),
422 .instanceSize = sizeof(Control),
423 .interfaceSize = sizeof(ControlInterface),
424 .initialize = initialize,
425 });
426 });
427
428 return clazz;
429}
430
431#undef _Class
const EnumName ControlBevelNames[]
Definition Control.c:28
const EnumName ControlSelectionNames[]
Definition Control.c:34
static void becomeKeyResponder(View *self)
Definition Control.c:132
Class * _Control(void)
Definition Control.c:414
static void stateDidChange(Control *self)
Definition Control.c:366
static bool acceptsKeyResponder(const View *self)
Definition Control.c:69
static View * init(View *self)
Definition Control.c:125
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition Control.c:109
static Control * initWithFrame(Control *self, const SDL_Rect *frame)
Definition Control.c:319
const EnumName ControlStateNames[]
Definition Control.c:40
static void respondToEvent(View *self, const SDL_Event *event)
Definition Control.c:283
static void applyStyle(View *self, const Style *style)
Definition Control.c:89
static bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)
Definition Control.c:149
static bool captureEvent(Control *self, const SDL_Event *event)
Definition Control.c:311
static bool isHighlighted(const Control *self)
Definition Control.c:350
static void render(View *self, Renderer *renderer)
Definition Control.c:181
static void dealloc(Object *self)
Definition Control.c:55
static void resignKeyResponder(View *self)
Definition Control.c:265
static bool isDisabled(const Control *self)
Definition Control.c:334
static void initialize(Class *clazz)
Definition Control.c:386
static bool acceptsTouchResponder(const View *self)
Definition Control.c:79
static bool isFocused(const Control *self)
Definition Control.c:342
static bool isSelected(const Control *self)
Definition Control.c:358
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:277
static void drawRoundedRect(const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *color)
Definition Renderer.c:407
static void drawRect(const Renderer *self, const SDL_Rect *rect, const SDL_Color *color)
Definition Renderer.c:365
static void drawBevel(const Renderer *self, const SDL_Rect *rect, int radius, int width, const SDL_Color *topLeft, const SDL_Color *bottomRight)
Definition Renderer.c:247
@ 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
@ InletTypeColor
Definition View+JSON.h:62
#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
Class * _View(void)
Definition View.c:2336
static void invalidateStyle(View *self)
Definition View.c:1092
static void setNeedsLayout(View *self)
Definition View.c:1865
static SDL_Rect renderFrame(const View *self)
Definition View.c:1629
static void emitViewEvent(View *self, ViewEvent code, ident data)
Definition View.c:794
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:117
SDL_Color bevelLightColor
The bevel color for the lit edges.
Definition Control.h:112
bool isDisabled(const Control *self)
Definition Control.c:334
SDL_Color bevelDarkColor
The bevel color for the shadowed edges.
Definition Control.h:106
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:149
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:75
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:225
int borderRadius
The corner radius applied to the background, border and bevel.
Definition View.h:281
bool matchesSelector(const View *self, const SimpleSelector *simpleSelector)