ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Select.c File Reference
#include <assert.h>
#include <Objectively/Array.h>
#include "Select.h"

Go to the source code of this file.

Macros

#define _Class   _Select
 

Functions

Class * _Select (void)
 
static void addOption (Select *self, const char *title, ident value)
 
static void addOption_addSubview (const Array *array, ident obj, ident data)
 ArrayEnumerator to add Options to the stackView.
 
static void addOption_removeSubview (const Array *array, ident obj, ident data)
 ArrayEnumerator to remove Options from the stackView.
 
static bool captureEvent (Control *self, const SDL_Event *event)
 
static void dealloc (Object *self)
 
static View * init (View *self)
 
static void initialize (Class *clazz)
 
static Select * initWithFrame (Select *self, const SDL_Rect *frame)
 
static void layoutSubviews (View *self)
 
static Option * optionWithValue (const Select *self, const ident value)
 
static bool optionWithValue_predicate (ident obj, ident data)
 Predicate function for optionWithValue.
 
static void removeAllOptions (Select *self)
 
static void removeOption (Select *self, Option *option)
 
static void removeOptionWithValue (Select *self, ident value)
 
static Option * selectedOption (const Select *self)
 
static Array * selectedOptions (const Select *self)
 
static bool selectedOptions_predicate (ident obj, ident data)
 Predicate for selectedOption and selectedOptions.
 
static void selectOption (Select *self, Option *option)
 
static void selectOption_enumerate (const Array *array, ident obj, ident data)
 ArrayEnumerator for enforcing ControlSelectionSingle.
 
static void selectOptionWithValue (Select *self, ident value)
 
static SDL_Size sizeThatFits (const View *self)
 
static void stateDidChange (Control *self)
 

Macro Definition Documentation

◆ _Class

#define _Class   _Select

Definition at line 30 of file Select.c.

Function Documentation

◆ _Select()

Class * _Select ( void  )

Definition at line 483 of file Select.c.

483 {
484 static Class *clazz;
485 static Once once;
486
487 do_once(&once, {
488 clazz = _initialize(&(const ClassDef) {
489 .name = "Select",
490 .superclass = _Control(),
491 .instanceSize = sizeof(Select),
492 .interfaceSize = sizeof(SelectInterface),
493 .initialize = initialize,
494 });
495 });
496
497 return clazz;
498}
Class * _Control(void)
Definition Control.c:414
static void initialize(Class *clazz)
Definition Select.c:456
A Control allowing users to select one or more Options.
Definition Select.h:63

◆ addOption()

static void addOption ( Select *  self,
const char *  title,
ident  value 
)
static

Definition at line 274 of file Select.c.

274 {
275
276 Option *option = $(alloc(Option), initWithTitle, title, value);
277 assert(option);
278
279 $(self->options, addObject, option);
280
281 $((Array *) self->options, enumerate, addOption_removeSubview, self->stackView);
282
283 if (self->comparator) {
284 $(self->options, sort, self->comparator);
285 }
286
287 $((Array *) self->options, enumerate, addOption_addSubview, self->stackView);
288
290 if ($(self, selectedOption) == NULL) {
291 $(option, setSelected, true);
292 }
293 }
294
295 option->view.nextResponder = (View *) self;
296
297 release(option);
298
299 $((View *) self, setNeedsLayout);
300}
static Button * initWithTitle(Button *self, const char *title)
Definition Button.c:175
static void setSelected(CollectionItemView *self, bool isSelected)
@ ControlSelectionSingle
Definition Control.h:56
static Option * selectedOption(const Select *self)
Definition Select.c:439
static void addOption_removeSubview(const Array *array, ident obj, ident data)
ArrayEnumerator to remove Options from the stackView.
Definition Select.c:259
static void addOption_addSubview(const Array *array, ident obj, ident data)
ArrayEnumerator to add Options to the stackView.
Definition Select.c:266
static void setNeedsLayout(View *self)
Definition View.c:1865
static void enumerate(View *self, ViewEnumerator enumerator, ident data)
Definition View.c:807
ControlSelection selection
The ControlSelection.
Definition Control.h:123
Select Options.
Definition Option.h:41
View view
The superclass.
Definition Option.h:46
Comparator comparator
An optional Comparator to sort Options.
Definition Select.h:79
Array * options
The Options.
Definition Select.h:89
StackView * stackView
The StackView for rendering the Options.
Definition Select.h:94
Control control
The superclass.
Definition Select.h:68
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:225
View * nextResponder
The next responder, or event handler, in the chain.
Definition View.h:392

◆ addOption_addSubview()

static void addOption_addSubview ( const Array *  array,
ident  obj,
ident  data 
)
static

ArrayEnumerator to add Options to the stackView.

Definition at line 266 of file Select.c.

266 {
267 $((View *) data, addSubview, obj);
268}
static void addSubview(View *self, View *subview)
Definition PageView.c:49

◆ addOption_removeSubview()

static void addOption_removeSubview ( const Array *  array,
ident  obj,
ident  data 
)
static

ArrayEnumerator to remove Options from the stackView.

Definition at line 259 of file Select.c.

259 {
260 $((View *) data, removeSubview, obj);
261}
static void removeSubview(View *self, View *subview)
Definition PageView.c:72

◆ captureEvent()

static bool captureEvent ( Control *  self,
const SDL_Event *  event 
)
static
See also
Control::captureEvent(Control *, const SDL_Event *)

Definition at line 137 of file Select.c.

137 {
138
139 Select *this = (Select *) self;
140
141 const Array *options = (Array *) this->options;
142
143 if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
144 if (self->state & ControlStateHighlighted) {
145 $((View *) self, becomeTouchResponder);
146 return true;
147 }
148 }
149
150 if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
151 if (self->state & ControlStateHighlighted) {
152 self->state &= ~ControlStateHighlighted;
153
154 for (size_t i = 0; i < options->count; i++) {
155
156 Option *option = $(options, objectAtIndex, i);
157 if ($((View *) option, didReceiveEvent, event)) {
158 $(this, selectOption, option);
159 if (this->delegate.didSelectOption) {
160 this->delegate.didSelectOption(this, option);
161 }
162 return true;
163 }
164 }
165 } else {
167 return true;
168 }
169 }
170
171 if (event->type == SDL_EVENT_KEY_DOWN) {
172
173 switch (event->key.key) {
174
175 case SDLK_ESCAPE:
176 self->state &= ~ControlStateHighlighted;
177 return true;
178
179 case SDLK_RETURN:
180 case SDLK_KP_ENTER:
182 return true;
183
184 case SDLK_UP:
185 case SDLK_DOWN: {
186 Option *option = $(this, selectedOption);
187 if (option) {
188
189 ssize_t index = $(options, indexOfObject, option);
190 if (event->key.key == SDLK_UP) {
191 index = (index - 1 + options->count) % options->count;
192 } else {
193 index = (index + 1) % options->count;
194 }
195
196 option = $(options, objectAtIndex, index);
197
198 $(this, selectOption, option);
199 if (this->delegate.didSelectOption) {
200 this->delegate.didSelectOption(this, option);
201 }
202 }
203 return true;
204 }
205 }
206 }
207
208 return super(Control, self, captureEvent, event);
209}
@ ControlStateHighlighted
Definition Control.h:67
static void selectOption(Select *self, Option *option)
Definition Select.c:401
static bool captureEvent(Control *self, const SDL_Event *event)
Definition Select.c:137
static void becomeTouchResponder(View *self)
Definition View.c:511
static bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition View.c:726
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

◆ dealloc()

static void dealloc ( Object *  self)
static
See also
Object::dealloc(Object *)

Definition at line 37 of file Select.c.

37 {
38
39 Select *this = (Select *) self;
40
41 memset(&this->delegate, 0, sizeof(this->delegate));
42
43 release(this->options);
44 release(this->stackView);
45
46 super(Object, self, dealloc);
47}
static void dealloc(Object *self)
Definition Select.c:37

◆ init()

static View * init ( View *  self)
static
See also
View::init(View *)

Definition at line 54 of file Select.c.

54 {
55 return (View *) $((Select *) self, initWithFrame, NULL);
56}
static Select * initWithFrame(Select *self, const SDL_Rect *frame)
Definition Select.c:306

◆ initialize()

static void initialize ( Class *  clazz)
static
See also
Class::initialize(Class *)

Definition at line 456 of file Select.c.

456 {
457
458 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
459
460 ((ViewInterface *) clazz->interface)->init = init;
461 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
462 ((ViewInterface *) clazz->interface)->sizeThatFits = sizeThatFits;
463
464 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
465 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
466
467 ((SelectInterface *) clazz->interface)->addOption = addOption;
468 ((SelectInterface *) clazz->interface)->initWithFrame = initWithFrame;
469 ((SelectInterface *) clazz->interface)->optionWithValue = optionWithValue;
470 ((SelectInterface *) clazz->interface)->removeAllOptions = removeAllOptions;
471 ((SelectInterface *) clazz->interface)->removeOption = removeOption;
472 ((SelectInterface *) clazz->interface)->removeOptionWithValue = removeOptionWithValue;
473 ((SelectInterface *) clazz->interface)->selectOption = selectOption;
474 ((SelectInterface *) clazz->interface)->selectOptionWithValue = selectOptionWithValue;
475 ((SelectInterface *) clazz->interface)->selectedOption = selectedOption;
476 ((SelectInterface *) clazz->interface)->selectedOptions = selectedOptions;
477}
static void removeOption(Select *self, Option *option)
Definition Select.c:358
static void selectOptionWithValue(Select *self, ident value)
Definition Select.c:421
static void stateDidChange(Control *self)
Definition Select.c:214
static View * init(View *self)
Definition Select.c:54
static void removeOptionWithValue(Select *self, ident value)
Definition Select.c:382
static SDL_Size sizeThatFits(const View *self)
Definition Select.c:113
static void addOption(Select *self, const char *title, ident value)
Definition Select.c:274
static void removeAllOptions(Select *self)
Definition Select.c:346
static Array * selectedOptions(const Select *self)
Definition Select.c:447
static Option * optionWithValue(const Select *self, const ident value)
Definition Select.c:336
static void layoutSubviews(View *self)
Definition Select.c:61
Option * optionWithValue(const Select *self, ident value)
Definition Select.c:336
layoutSubviews(View *self)
Arranges this View's immediate subviews within its (already final) frame.
Definition Box.c:74

◆ initWithFrame()

static Select * initWithFrame ( Select *  self,
const SDL_Rect *  frame 
)
static

Definition at line 306 of file Select.c.

306 {
307
308 self = (Select *) super(Control, self, initWithFrame, frame);
309 if (self) {
310
312
313 self->options = $$(Array, arrayWithCapacity, 8);
314 assert(self->options);
315
316 self->stackView = $(alloc(StackView), initWithFrame, NULL);
317 assert(self->stackView);
318
319 $((View *) self, addSubview, (View *) self->stackView);
320 }
321
322 return self;
323}
StackViews are containers that manage the arrangement of their subviews.
Definition StackView.h:68

◆ layoutSubviews()

static void layoutSubviews ( View *  self)
static
See also
View::layoutSubviews(View *)

Definition at line 61 of file Select.c.

61 {
62
63 Select *this = (Select *) self;
64
65 Control *control = (Control *) self;
66
67 if (control->selection == ControlSelectionSingle) {
68
69 const Array *options = (Array *) this->options;
70 for (size_t i = 0; i < options->count; i++) {
71
72 Option *option = $(options, objectAtIndex, i);
73 if (option->isSelected) {
75 } else if ($(control, isHighlighted)) {
77 } else {
79 }
80 }
81
82 $((View *) this->stackView, sizeToFit);
83 $((View *) this->stackView, layoutIfNeeded);
84
85 // Keep the expanded menu on-screen. The options are only un-hidden (and the menu
86 // sized to its full height) just above, so this is the earliest point the true
87 // height is known -- stateDidChange sees a collapsed, all-hidden menu. The menu
88 // opens downward from the control and is parented to the window root (its frame
89 // is window space); if it runs past the bottom edge, shift it up, clamped to the
90 // top. Idempotent: once its bottom sits at the window edge, re-running is a no-op.
91 View *menu = (View *) this->stackView;
92 if ($(control, isHighlighted) && menu->window) {
93
94 int windowHeight;
95 SDL_GetWindowSize(menu->window, NULL, &windowHeight);
96
97 const int bottom = menu->frame.y + menu->frame.h;
98 if (bottom > windowHeight) {
99 menu->frame.y -= (bottom - windowHeight);
100 if (menu->frame.y < 0) {
101 menu->frame.y = 0;
102 }
103 }
104 }
105 }
106
107 super(View, self, layoutSubviews);
108}
static bool isHighlighted(const Control *self)
Definition Control.c:350
static void layoutIfNeeded(View *self)
Definition View.c:1174
static void setVisibility(View *self, ViewVisibility visibility)
Definition View.c:1834
static void sizeToFit(View *self)
Definition View.c:2018
@ ViewVisibilityVisible
Definition View.h:121
@ ViewVisibilityHidden
Definition View.h:122
bool isSelected
True if this Option is selected, false otherwise.
Definition Option.h:57
SDL_Window * window
The window.
Definition View.h:480
SDL_Rect frame
The frame, relative to the superview.
Definition View.h:301

◆ optionWithValue()

static Option * optionWithValue ( const Select *  self,
const ident  value 
)
static

Definition at line 336 of file Select.c.

336 {
337 return $((Array *) self->options, find, optionWithValue_predicate, value);
338}
static bool optionWithValue_predicate(ident obj, ident data)
Predicate function for optionWithValue.
Definition Select.c:328

◆ optionWithValue_predicate()

static bool optionWithValue_predicate ( ident  obj,
ident  data 
)
static

Predicate function for optionWithValue.

Definition at line 328 of file Select.c.

328 {
329 return ((Option *) obj)->value == data;
330}

◆ removeAllOptions()

static void removeAllOptions ( Select *  self)
static

Definition at line 346 of file Select.c.

346 {
347
348 Option *option;
349 while ((option = $((Array *) self->options, lastObject))) {
350 $(self, removeOption, option);
351 }
352}

◆ removeOption()

static void removeOption ( Select *  self,
Option *  option 
)
static

Definition at line 358 of file Select.c.

358 {
359
360 if ($((Array *) self->options, containsObject, option)) {
361
362 $(self->options, removeObject, option);
363 $((View *) self->stackView, removeSubview, (View *) option);
364
366 if ($(self, selectedOption) == NULL) {
367 Option *first_option = $((Array *) self->options, firstObject);
368 if (first_option) {
369 $(self, selectOption, first_option);
370 }
371 }
372 }
373
374 $((View *) self, setNeedsLayout);
375 }
376}

◆ removeOptionWithValue()

static void removeOptionWithValue ( Select *  self,
ident  value 
)
static

Definition at line 382 of file Select.c.

382 {
383
384 Option *option = (Option *) $(self, optionWithValue, value);
385 if (option) {
386 $(self, removeOption, option);
387 }
388}

◆ selectedOption()

static Option * selectedOption ( const Select *  self)
static

Definition at line 439 of file Select.c.

439 {
440 return $((Array *) self->options, find, selectedOptions_predicate, NULL);
441}
static bool selectedOptions_predicate(ident obj, ident data)
Predicate for selectedOption and selectedOptions.
Definition Select.c:431

◆ selectedOptions()

static Array * selectedOptions ( const Select *  self)
static

Definition at line 447 of file Select.c.

447 {
448 return $((Array *) self->options, filteredArray, selectedOptions_predicate, NULL);
449}

◆ selectedOptions_predicate()

static bool selectedOptions_predicate ( ident  obj,
ident  data 
)
static

Predicate for selectedOption and selectedOptions.

Definition at line 431 of file Select.c.

431 {
432 return ((Option *) obj)->isSelected;
433}

◆ selectOption()

static void selectOption ( Select *  self,
Option *  option 
)
static

Definition at line 401 of file Select.c.

401 {
402
404 $((Array *) self->options, enumerate, selectOption_enumerate, NULL);
405 }
406
407 if (option) {
408
409 if (option && option->isSelected == false) {
410 $(option, setSelected, true);
411 }
412 }
413
414 $((View *) self, setNeedsLayout);
415}
static void selectOption_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for enforcing ControlSelectionSingle.
Definition Select.c:393

◆ selectOption_enumerate()

static void selectOption_enumerate ( const Array *  array,
ident  obj,
ident  data 
)
static

ArrayEnumerator for enforcing ControlSelectionSingle.

Definition at line 393 of file Select.c.

393 {
394 $((Option *) obj, setSelected, false);
395}

◆ selectOptionWithValue()

static void selectOptionWithValue ( Select *  self,
ident  value 
)
static

Definition at line 421 of file Select.c.

421 {
422
423 Option *option = $(self, optionWithValue, value);
424
425 $(self, selectOption, option);
426}

◆ sizeThatFits()

static SDL_Size sizeThatFits ( const View *  self)
static
See also
View::sizeThatFits(const View *)

Definition at line 113 of file Select.c.

113 {
114
115 const Select *this = (Select *) self;
116
117 SDL_Size size = super(View, self, sizeThatFits);
118
119 const Array *options = (Array *) this->options;
120 for (size_t i = 0; i < options->count; i++) {
121
122 const View *option = $(options, objectAtIndex, i);
123 const SDL_Size optionSize = $(option, sizeThatFits);
124
125 size.w = max(size.w, optionSize.w + self->padding.left + self->padding.right);
126 size.h = max(size.h, optionSize.h + self->padding.top + self->padding.bottom);
127 }
128
129 return size;
130}
static SDL_Size size(const Image *self)
Definition Image.c:416
ViewPadding padding
The padding.
Definition View.h:398
int top
Definition View.h:170
int bottom
Definition View.h:170
int right
Definition View.h:170
int left
Definition View.h:170

◆ stateDidChange()

static void stateDidChange ( Control *  self)
static
See also
Control::stateDidChange(Control *)

Definition at line 214 of file Select.c.

214 {
215
216 Select *this = (Select *) self;
217
218 if (self->selection == ControlSelectionSingle) {
219
220 View *stackView = (View *) this->stackView;
221
222 if ($(self, isHighlighted)) {
223
224 const SDL_Rect renderFrame = $((View *) self, renderFrame);
225
226 stackView->frame.x = renderFrame.x + self->view.padding.left;
227 stackView->frame.y = renderFrame.y + self->view.padding.top;
228
229 View *view = (View *) self;
230
231 while (view->superview) {
232 view = view->superview;
233 }
234
235 $(stackView, addClassName, "options");
236 $(view, addSubview, stackView);
237
238 stackView->nextResponder = (View *) self;
239
240 } else {
241
242 $(stackView, removeClassName, "options");
243 $((View *) self, addSubview, stackView);
244
245 stackView->nextResponder = NULL;
246 }
247
248 $(stackView, sizeToFit);
249 }
250
251 super(Control, self, stateDidChange);
252}
static void removeClassName(View *self, const char *className)
Definition View.c:1445
static void addClassName(View *self, const char *className)
Definition View.c:183
static SDL_Rect renderFrame(const View *self)
Definition View.c:1629
View view
The superclass.
Definition Control.h:88
View * superview
The super View.
Definition View.h:454