ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
Select.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 <Objectively/Array.h>
27
28#include "Select.h"
29
30#define _Class _Select
31
32#pragma mark - Object
33
37static void dealloc(Object *self) {
38
39 Select *this = (Select *) self;
40
41 release(this->options);
42 release(this->stackView);
43
44 super(Object, self, dealloc);
45}
46
47#pragma mark - View
48
52static View *init(View *self) {
53 return (View *) $((Select *) self, initWithFrame, NULL);
54}
55
59static void layoutSubviews(View *self) {
60
61 Select *this = (Select *) self;
62
63 Control *control = (Control *) self;
64
65 if (control->selection == ControlSelectionSingle) {
66
67 const Array *options = (Array *) this->options;
68 for (size_t i = 0; i < options->count; i++) {
69
70 Option *option = $(options, objectAtIndex, i);
71 if (option->isSelected) {
72 option->view.hidden = false;
73 } else if ($(control, isHighlighted)) {
74 option->view.hidden = false;
75 } else {
76 option->view.hidden = true;
77 }
78 }
79
80 $((View *) this->stackView, sizeToFit);
81 }
82
83 super(View, self, layoutSubviews);
84}
85
89static SDL_Size sizeThatFits(const View *self) {
90
91 const Select *this = (Select *) self;
92
93 SDL_Size size = super(View, self, sizeThatFits);
94
95 const Array *options = (Array *) this->options;
96 for (size_t i = 0; i < options->count; i++) {
97
98 const View *option = $(options, objectAtIndex, i);
99 const SDL_Size optionSize = $(option, sizeThatFits);
100
101 size.w = max(size.w, optionSize.w + self->padding.left + self->padding.right);
102 size.h = max(size.h, optionSize.h + self->padding.top + self->padding.bottom);
103 }
104
105 return size;
106}
107
108#pragma mark - Control
109
113static _Bool captureEvent(Control *self, const SDL_Event *event) {
114
115 Select *this = (Select *) self;
116
117 if (event->type == SDL_MOUSEBUTTONUP) {
118 if (self->state == ControlStateHighlighted) {
119 self->state &= ~ControlStateHighlighted;
120
121 const Array *options = (Array *) this->options;
122 for (size_t i = 0; i < options->count; i++) {
123
124 Option *option = $(options, objectAtIndex, i);
125 if ($((View *) option, didReceiveEvent, event)) {
126 $(this, selectOption, option);
127 if (this->delegate.didSelectOption) {
128 this->delegate.didSelectOption(this, option);
129 }
130 return true;
131 }
132 }
133 } else if ($((View *) self, didReceiveEvent, event)) {
134 self->state |= ControlStateHighlighted;
135 return true;
136 }
137 }
138
139 return super(Control, self, captureEvent, event);
140}
141
145static void stateDidChange(Control *self) {
146
147 Select *this = (Select *) self;
148
149 if (self->selection == ControlSelectionSingle) {
150
151 View *stackView = (View *) this->stackView;
152
153 if ($(self, isHighlighted)) {
154
155 const SDL_Rect renderFrame = $((View *) self, renderFrame);
156
157 stackView->frame.x = renderFrame.x + self->view.padding.left;
158 stackView->frame.y = renderFrame.y + self->view.padding.top;
159
160 View *view = (View *) self;
161
162 while (view->superview) {
163 view = view->superview;
164 }
165
166 $(stackView, addClassName, "options");
167 $(view, addSubview, stackView);
168
169 stackView->nextResponder = (View *) self;
170
171 } else {
172
173 $(stackView, removeClassName, "options");
174 $((View *) self, addSubview, stackView);
175
176 stackView->nextResponder = NULL;
177 }
178
179 $(stackView, sizeToFit);
180 }
181
182 super(Control, self, stateDidChange);
183}
184
185#pragma mark - Select
186
190static void addOption_removeSubview(const Array *array, ident obj, ident data) {
191 $((View *) data, removeSubview, obj);
192}
193
197static void addOption_addSubview(const Array *array, ident obj, ident data) {
198 $((View *) data, addSubview, obj);
199}
200
205static void addOption(Select *self, const char *title, ident value) {
206
207 Option *option = $(alloc(Option), initWithTitle, title, value);
208 assert(option);
209
210 $(self->options, addObject, option);
211
212 $((Array *) self->options, enumerateObjects, addOption_removeSubview, self->stackView);
213
214 if (self->comparator) {
215 $(self->options, sort, self->comparator);
216 }
217
218 $((Array *) self->options, enumerateObjects, addOption_addSubview, self->stackView);
219
221 if ($(self, selectedOption) == NULL) {
222 $(option, setSelected, true);
223 }
224 }
225
226 release(option);
227
228 self->control.view.needsLayout = true;
229}
230
235static Select *initWithFrame(Select *self, const SDL_Rect *frame) {
236
237 self = (Select *) super(Control, self, initWithFrame, frame);
238 if (self) {
239
241
242 self->options = $$(MutableArray, arrayWithCapacity, 8);
243 assert(self->options);
244
245 self->stackView = $(alloc(StackView), initWithFrame, NULL);
246 assert(self->stackView);
247
248 $((View *) self, addSubview, (View *) self->stackView);
249 }
250
251 return self;
252}
253
257static _Bool optionWithValue_predicate(ident obj, ident data) {
258 return ((Option *) obj)->value == data;
259}
260
265static Option *optionWithValue(const Select *self, const ident value) {
266 return $((Array *) self->options, findObject, optionWithValue_predicate, value);
267}
268
272static void removeAllOptions_enumerate(const Array *array, ident obj, ident data) {
273 $((Select *) data, removeOption, obj);
274}
275
280static void removeAllOptions(Select *self) {
281 $((Array *) self->options, enumerateObjects, removeAllOptions_enumerate, self);
282}
283
288static void removeOption(Select *self, Option *option) {
289
290 if ($((Array *) self->options, containsObject, option)) {
291
292 $(self->options, removeObject, option);
293 $((View *) self->stackView, removeSubview, (View *) option);
294
296 if ($(self, selectedOption) == NULL) {
297 Option *first_option = $((Array *) self->options, firstObject);
298 if (first_option) {
299 $(self, selectOption, first_option);
300 }
301 }
302 }
303
304 self->control.view.needsLayout = true;
305 }
306}
307
312static void removeOptionWithValue(Select *self, ident value) {
313
314 Option *option = (Option *) $(self, optionWithValue, value);
315 if (option) {
316 $(self, removeOption, option);
317 }
318}
319
323static void selectOption_enumerate(const Array *array, ident obj, ident data) {
324 $((Option *) obj, setSelected, false);
325}
326
331static void selectOption(Select *self, Option *option) {
332
334 $((Array *) self->options, enumerateObjects, selectOption_enumerate, NULL);
335 }
336
337 if (option) {
338
339 if (option && option->isSelected == false) {
340 $(option, setSelected, true);
341 }
342 }
343
344 self->control.view.needsLayout = true;
345}
346
351static void selectOptionWithValue(Select *self, ident value) {
352
353 Option *option = $(self, optionWithValue, value);
354
355 $(self, selectOption, option);
356}
357
361static _Bool selectedOptions_predicate(ident obj, ident data) {
362 return ((Option *) obj)->isSelected;
363}
364
369static Option *selectedOption(const Select *self) {
370 return $((Array *) self->options, findObject, selectedOptions_predicate, NULL);
371}
372
377static Array *selectedOptions(const Select *self) {
378 return $((Array *) self->options, filteredArray, selectedOptions_predicate, NULL);
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)->init = init;
391 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
392 ((ViewInterface *) clazz->interface)->sizeThatFits = sizeThatFits;
393
394 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
395 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
396
397 ((SelectInterface *) clazz->interface)->addOption = addOption;
398 ((SelectInterface *) clazz->interface)->initWithFrame = initWithFrame;
399 ((SelectInterface *) clazz->interface)->optionWithValue = optionWithValue;
400 ((SelectInterface *) clazz->interface)->removeAllOptions = removeAllOptions;
401 ((SelectInterface *) clazz->interface)->removeOption = removeOption;
402 ((SelectInterface *) clazz->interface)->removeOptionWithValue = removeOptionWithValue;
403 ((SelectInterface *) clazz->interface)->selectOption = selectOption;
404 ((SelectInterface *) clazz->interface)->selectOptionWithValue = selectOptionWithValue;
405 ((SelectInterface *) clazz->interface)->selectedOption = selectedOption;
406 ((SelectInterface *) clazz->interface)->selectedOptions = selectedOptions;
407}
408
413Class *_Select(void) {
414 static Class *clazz;
415 static Once once;
416
417 do_once(&once, {
418 clazz = _initialize(&(const ClassDef) {
419 .name = "Select",
420 .superclass = _Control(),
421 .instanceSize = sizeof(Select),
422 .interfaceOffset = offsetof(Select, interface),
423 .interfaceSize = sizeof(SelectInterface),
425 });
426 });
427
428 return clazz;
429}
430
431#undef _Class
432
@ ControlSelectionSingle
Definition: Control.h:57
@ ControlStateHighlighted
Definition: Control.h:68
static void removeAllOptions_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for removeAllOptions.
Definition: Select.c:272
static void selectOption_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for enforcing ControlSelectionSingle.
Definition: Select.c:323
static void addOption_removeSubview(const Array *array, ident obj, ident data)
ArrayEnumerator to remove Options from the stackView.
Definition: Select.c:190
static _Bool optionWithValue_predicate(ident obj, ident data)
Predicate function for optionWithValue.
Definition: Select.c:257
static void dealloc(Object *self)
Definition: Select.c:37
static void addOption_addSubview(const Array *array, ident obj, ident data)
ArrayEnumerator to add Options to the stackView.
Definition: Select.c:197
static void initialize(Class *clazz)
Definition: Select.c:386
static _Bool selectedOptions_predicate(ident obj, ident data)
Predicate for selectedOption and selectedOptions.
Definition: Select.c:361
A Control allowing users to select one or more Options.
Box * initWithFrame(Box *self, const SDL_Rect *frame)
Initializes this Box with the given frame.
Definition: Box.c:92
Button * initWithTitle(Button *self, const char *title)
Initializes this Button with the specified title.
Definition: Button.c:135
void setSelected(CollectionItemView *self, _Bool isSelected)
Sets the selected state of this item.
CollectionView * init(CollectionView *self, const SDL_Rect *frame)
Initializes this CollectionView with the specified frame and style.
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
Class * _Control(void)
The Control archetype.
Definition: Control.c:379
unsigned int state
The bit mask of ControlState.
Definition: Control.h:110
View view
The superclass.
Definition: Control.h:88
_Bool isHighlighted(const Control *self)
Definition: Control.c:317
ControlSelection selection
The ControlSelection.
Definition: Control.h:115
SDL_Size size(const Image *self)
Definition: Image.c:181
Select Options.
Definition: Option.h:41
_Bool isSelected
True if this Option is selected, false otherwise.
Definition: Option.h:57
View view
The superclass.
Definition: Option.h:46
The SDL_Size type.
Definition: Types.h:62
int w
Definition: Types.h:63
int h
Definition: Types.h:63
A Control allowing users to select one or more Options.
Definition: Select.h:63
Array * selectedOptions(const Select *self)
Definition: Select.c:377
void removeOptionWithValue(Select *self, ident value)
Removes first the Option with the given value.
Definition: Select.c:312
void addOption(Select *self, const char *title, ident value)
Creates and adds a new Option to this Select.
Definition: Select.c:205
MutableArray * options
The Options.
Definition: Select.h:89
void selectOption(Select *self, Option *option)
Selects the given Option.
Definition: Select.c:331
Option * selectedOption(const Select *self)
Definition: Select.c:369
void selectOptionWithValue(Select *self, ident value)
Selects the first Option with the given value.
Definition: Select.c:351
Comparator comparator
An optional Comparator to sort Options.
Definition: Select.h:79
void removeOption(Select *self, Option *option)
Removes the specified Option.
Definition: Select.c:288
StackView * stackView
The StackView for rendering the Options.
Definition: Select.h:94
Option * optionWithValue(const Select *self, ident value)
Definition: Select.c:265
Class * _Select(void)
The Select archetype.
Definition: Select.c:413
void removeAllOptions(Select *self)
Removes all Options from this Select.
Definition: Select.c:280
Control control
The superclass.
Definition: Select.h:68
StackViews are containers that manage the arrangement of their subviews.
Definition: StackView.h:68
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition: View.h:133
View * superview
The super View.
Definition: View.h:258
_Bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition: View.h:221
_Bool hidden
If true, this View is not drawn.
Definition: View.h:195
void addSubview(View *self, View *subview)
Adds a subview to this view, to be drawn above its siblings.
Definition: PageView.c:35
void addClassName(View *self, const char *className)
Adds the given class name to this View.
Definition: View.c:120
_Bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition: View.c:546
View * nextResponder
The next responder, or event handler, in the chain.
Definition: View.h:228
void sizeThatFits(const View *self)
ViewPadding padding
The padding.
Definition: View.h:233
SDL_Rect renderFrame(const View *self)
Definition: View.c:1275
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
Definition: Box.c:74
SDL_Rect frame
The frame, relative to the superview.
Definition: View.h:190
void sizeToFit(View *self)
Resizes this View to fit its subviews.
Definition: View.c:1496
void removeClassName(View *self, const char *className)
Removes the given class name to this View.
Definition: View.c:1158
void removeSubview(View *self, View *subview)
Removes the given subview from this View.
Definition: PageView.c:58
int top
Definition: View.h:100
int bottom
Definition: View.h:100
int right
Definition: View.h:100
int left
Definition: View.h:100