ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
TabView.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#include <string.h>
26
27#include <Objectively/String.h>
28
29#include "TabView.h"
30
31#define _Class _TabView
32
33#pragma mark - Object
34
38static void dealloc(Object *self) {
39
40 TabView *this = (TabView *) self;
41
42 memset(&this->delegate, 0, sizeof(this->delegate));
43
44 release(this->tabPageView);
45 release(this->tabSelectionView);
46 release(this->tabs);
47
48 super(Object, self, dealloc);
49}
50
51#pragma mark - View
52
56static void awakeWithDictionary_tabs(const Array *array, ident obj, ident data) {
57
58 const String *identifier = $((Dictionary *) obj, objectForKeyPath, "identifier");
59 assert(identifier);
60
61 TabViewItem *tab = $(alloc(TabViewItem), initWithIdentifier, identifier->chars);
62 assert(tab);
63
64 const Inlet inlets[] = MakeInlets(
65 MakeInlet("label", InletTypeView, &tab->label, NULL),
66 MakeInlet("view", InletTypeView, &tab->view, NULL)
67 );
68
69 $((View *) data, bind, inlets, obj);
70 $((TabView *) data, addTab, tab);
71
72 release(tab);
73}
74
78static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
79
80 super(View, self, awakeWithDictionary, dictionary);
81
82 TabView *this = (TabView *) self;
83
84 const Inlet inlets[] = MakeInlets(
85 MakeInlet("tabPageView", InletTypeView, &this->tabPageView, NULL),
86 MakeInlet("tabSelectionView", InletTypeView, &this->tabSelectionView, NULL)
87 );
88
89 $(self, bind, inlets, dictionary);
90
91 const Array *tabs = $(dictionary, objectForKeyPath, "tabs");
92 if (tabs) {
93 $(tabs, enumerate, awakeWithDictionary_tabs, self);
94 }
95}
96
100static View *init(View *self) {
101 return (View *) $((TabView *) self, initWithFrame, NULL);
102}
103
107static void respondToEvent(View *self, const SDL_Event *event) {
108
109 if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
110
111 TabView *this = (TabView *) self;
112
113 const Array *tabs = (Array *) this->tabs;
114 for (size_t i = 0; i < tabs->count; i++) {
115
116 TabViewItem *tab = $(tabs, objectAtIndex, i);
117
118 if ($((View *) tab->label, didReceiveEvent, event)) {
119 $(this, selectTab, tab);
120 }
121 }
122 }
123
124 super(View, self, respondToEvent, event);
125}
126
127#pragma mark - TabView
128
133static void addTab(TabView *self, TabViewItem *tab) {
134
135 assert(tab);
136
137 $(self->tabs, addObject, tab);
138
139 $((View *) self->tabSelectionView, addSubview, (View *) tab->label);
140 $((View *) self->tabPageView, addSubview, tab->view);
141
142 if (self->delegate.didAddTab) {
143 self->delegate.didAddTab(self, tab);
144 }
145
146 if (self->selectedTab == NULL) {
147 $(self, selectTab, tab);
148 }
149}
150
155static TabView *initWithFrame(TabView *self, const SDL_Rect *frame) {
156
157 self = (TabView *) super(StackView, self, initWithFrame, frame);
158 if (self) {
159
160 self->tabPageView = $(alloc(PageView), initWithFrame, NULL);
161 assert(self->tabPageView);
162
163 self->tabs = $$(Array, array);
164 assert(self->tabs);
165
166 self->tabSelectionView = $(alloc(StackView), initWithFrame, NULL);
167 assert(self->tabSelectionView);
168
169 $((View *) self->tabSelectionView, addClassName, "tabSelectionView");
170 $((View *) self, addSubview, (View *) self->tabSelectionView);
171
172 $((View *) self->tabPageView, addClassName, "tabPageView");
173 $((View *) self, addSubview, (View *) self->tabPageView);
174 }
175
176 return self;
177}
178
183static void removeTab(TabView *self, TabViewItem *tab) {
184
185 assert(tab);
186
187 if (self->delegate.willRemoveTab) {
188 self->delegate.willRemoveTab(self, tab);
189 }
190
191 retain(tab);
192
193 $(self->tabs, removeObject, tab);
194
195 $((View *) self->tabSelectionView, removeSubview, (View *) tab->label);
196 $((View *) self->tabPageView, removeSubview, tab->view);
197
198 if (self->selectedTab == tab) {
199 $(self, selectTab, NULL);
200 }
201
202 release(tab);
203}
204
208static void selectTab_enumerate(const Array *array, ident obj, ident data) {
209
210 TabViewItem *tab = obj;
211
212 int state = tab->state;
213
214 if (tab == ((TabView *) data)->selectedTab) {
215 state |= TabStateSelected;
216 } else {
217 state &= ~TabStateSelected;
218 }
219
220 $(tab, setState, state);
221}
222
227static void selectTab(TabView *self, TabViewItem *tab) {
228
229 if (self->selectedTab != tab) {
230
231 const Array *tabs = (Array *) self->tabs;
232
233 if (tab) {
234 self->selectedTab = tab;
235 } else {
236 self->selectedTab = $(tabs, firstObject);
237 }
238
239 $(tabs, enumerate, selectTab_enumerate, self);
240
241 if (self->selectedTab) {
242 $(self->tabPageView, setCurrentPage, self->selectedTab->view);
243
244 if (self->delegate.didSelectTab) {
245 self->delegate.didSelectTab(self, self->selectedTab);
246 }
247 }
248
249 self->stackView.view.needsLayout = true;
250 }
251}
252
256static bool tabWithIdentifier_predicate(const ident obj, ident data) {
257
258 const TabViewItem *tab = obj;
259
260 if (tab->identifier) {
261 if (data) {
262 return !strcmp(tab->identifier, data);
263 } else {
264 return false;
265 }
266 } else {
267 return data == NULL;
268 }
269}
270
275static TabViewItem *tabWithIdentifier(const TabView *self, const char *identifier) {
276 return $((Array *) self->tabs, find, tabWithIdentifier_predicate, (ident) identifier);
277}
278
279#pragma mark - Class lifecycle
280
284static void initialize(Class *clazz) {
285
286 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
287
288 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
289 ((ViewInterface *) clazz->interface)->init = init;
290 ((ViewInterface *) clazz->interface)->respondToEvent = respondToEvent;
291
292 ((TabViewInterface *) clazz->interface)->addTab = addTab;
293 ((TabViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
294 ((TabViewInterface *) clazz->interface)->removeTab = removeTab;
295 ((TabViewInterface *) clazz->interface)->selectTab = selectTab;
296 ((TabViewInterface *) clazz->interface)->tabWithIdentifier = tabWithIdentifier;
297}
298
303Class *_TabView(void) {
304 static Class *clazz;
305 static Once once;
306
307 do_once(&once, {
308 clazz = _initialize(&(const ClassDef) {
309 .name = "TabView",
310 .superclass = _StackView(),
311 .instanceSize = sizeof(TabView),
312 .interfaceOffset = offsetof(TabView, interface),
313 .interfaceSize = sizeof(TabViewInterface),
315 });
316 });
317
318 return clazz;
319}
320
321#undef _Class
static void setCurrentPage(PageView *self, View *currentPage)
Definition PageView.c:130
static void removeSubview(View *self, View *subview)
Definition PageView.c:72
static void addSubview(View *self, View *subview)
Definition PageView.c:49
Class * _StackView(void)
Definition StackView.c:287
static void addTab(TabView *self, TabViewItem *tab)
Definition TabView.c:133
Class * _TabView(void)
Definition TabView.c:303
static TabView * initWithFrame(TabView *self, const SDL_Rect *frame)
Definition TabView.c:155
static View * init(View *self)
Definition TabView.c:100
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition TabView.c:78
static void respondToEvent(View *self, const SDL_Event *event)
Definition TabView.c:107
static void awakeWithDictionary_tabs(const Array *array, ident obj, ident data)
ArrayEnumerator for awaking TabViewItems.
Definition TabView.c:56
static void removeTab(TabView *self, TabViewItem *tab)
Definition TabView.c:183
static void selectTab_enumerate(const Array *array, ident obj, ident data)
ArrayEnumerator for selectTab.
Definition TabView.c:208
static void selectTab(TabView *self, TabViewItem *tab)
Definition TabView.c:227
static bool tabWithIdentifier_predicate(const ident obj, ident data)
NULL-safe Predicate for tabWithIdentifier.
Definition TabView.c:256
static void dealloc(Object *self)
Definition TabView.c:38
static TabViewItem * tabWithIdentifier(const TabView *self, const char *identifier)
Definition TabView.c:275
static void initialize(Class *clazz)
Definition TabView.c:284
Tabbed pages within a single View.
static void setState(TabViewItem *self, int state)
Definition TabViewItem.c:93
@ TabStateSelected
Definition TabViewItem.h:41
static TableColumn * initWithIdentifier(TableColumn *self, const char *identifier)
Definition TableColumn.c:56
@ InletTypeView
Definition View+JSON.h:139
#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
static void addClassName(View *self, const char *className)
Definition View.c:159
static bool didReceiveEvent(const View *self, const SDL_Event *event)
Definition View.c:668
static void enumerate(View *self, ViewEnumerator enumerator, ident data)
Definition View.c:749
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:155
PageViews manage their subviews as pages in a book.
Definition PageView.h:60
StackViews are containers that manage the arrangement of their subviews.
Definition StackView.h:68
View view
The superclass.
Definition StackView.h:73
void(* didAddTab)(TabView *tabView, TabViewItem *tab)
Called when a tab is added.
Definition TabView.h:57
void(* didSelectTab)(TabView *tabView, TabViewItem *tab)
Called when a tab is selected.
Definition TabView.h:64
void(* willRemoveTab)(TabView *tabView, TabViewItem *tab)
Called when a tab is to be removed.
Definition TabView.h:71
TabViews allow for the display of multiple pages of information within a single view.
Definition TabView.h:79
StackView stackView
The superclass.
Definition TabView.h:84
PageView * tabPageView
The tab content container.
Definition TabView.h:105
TabViewItem * selectedTab
The selected TabViewItem.
Definition TabView.h:100
void removeTab(TabView *self, TabViewItem *tab)
Removes the specified TabViewItem from this TabView.
Definition TabView.c:183
StackView * tabSelectionView
The tab selection container.
Definition TabView.h:110
Array * tabs
The TabViewItems.
Definition TabView.h:115
TabViewDelegate delegate
The TabViewDelegate.
Definition TabView.h:95
TabViewItems embed Views in a TabView.
Definition TabViewItem.h:51
int state
The bit mask of TabState.
Definition TabViewItem.h:77
View * view
The View this TabViewItem embeds.
Definition TabViewItem.h:82
Label * label
The Label used to select this tab.
Definition TabViewItem.h:72
char * identifier
The identifier.
Definition TabViewItem.h:67
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:134
bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition View.h:222
void respondToEvent(View *self, const SDL_Event *event)
Responds to the specified event.
Definition Control.c:266