ObjectivelyMVC 0.1.0
Object oriented MVC framework for OpenGL, SDL2 and GNU C
WindowController.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/String.h>
27
28#include "Log.h"
29#include "WindowController.h"
30
31#define _Class _WindowController
32
33#pragma mark - Object
34
38static void dealloc(Object *self) {
39
40 WindowController *this = (WindowController *) self;
41
42 release(this->debugViewController);
43 release(this->renderer);
44 release(this->viewController);
45
46 super(Object, self, dealloc);
47}
48
49#pragma mark - WindowController
50
55static void debug(WindowController *self) {
56
57 if (self->viewController && self->debugViewController) {
58
59 ViewController *debugViewController = (ViewController *) self->debugViewController;
60
61 SDL_Point point = MakePoint(0, 0);
62 SDL_GetMouseState(&point.x, &point.y);
63
64 View *view = $(self->viewController->view, hitTest, &point);
65 if (view) {
66 $(self->debugViewController, debug, view, self->renderer);
67 }
68
69 $(debugViewController->view, applyThemeIfNeeded, self->theme);
70 $(debugViewController->view, layoutIfNeeded);
71 $(debugViewController->view, draw, self->renderer);
72 }
73}
74
79static View *eventTarget(const WindowController *self, const SDL_Event *event) {
80
81 SDL_Point point;
82
83 if (event->type == SDL_MOUSEBUTTONDOWN || event->type == SDL_MOUSEBUTTONUP) {
84 point = MakePoint(event->button.x, event->button.y);
85 } else if (event->type == SDL_MOUSEMOTION) {
86 point = MakePoint(event->motion.x, event->motion.y);
87 } else if (event->type == SDL_MOUSEWHEEL) {
88 SDL_GetMouseState(&point.x, &point.y);
89 } else {
90 return NULL;
91 }
92
93 return $(self->viewController->view, hitTest, &point);
94}
95
100static View *firstResponder(const WindowController *self, const SDL_Event *event) {
101 return $$(View, firstResponder, self->window) ?: $(self, eventTarget, event);
102}
103
108static WindowController *initWithWindow(WindowController *self, SDL_Window *window) {
109
110 self = (WindowController *) super(Object, self, init);
111 if (self) {
112 $(self, setWindow, window);
113 $(self, setViewController, NULL);
114 $(self, setSoundStage, NULL);
115 $(self, setRenderer, NULL);
116 $(self, setTheme, NULL);
117 }
118
119 return self;
120}
121
126static void render(WindowController *self) {
127
128 assert(self->renderer);
129
130 $(self->renderer, beginFrame);
131
134
135 $(self->viewController->view, draw, self->renderer);
136
137 $(self, debug);
138
139 $(self->renderer, endFrame);
140}
141
146static void respondToEvent(WindowController *self, const SDL_Event *event) {
147
148 if (event->type == SDL_USEREVENT && event->user.type == MVC_NOTIFICATION_EVENT) {
150 .name = event->user.code,
151 .sender = event->user.data1,
152 .data = event->user.data2
153 });
154 } else {
155
156 if (event->type == SDL_WINDOWEVENT) {
157 switch (event->window.event) {
158 case SDL_WINDOWEVENT_EXPOSED:
159 $(self, setWindow, SDL_GL_GetCurrentWindow());
163 break;
164 case SDL_WINDOWEVENT_CLOSE:
167 break;
168 default:
169 break;
170 }
171 }
172
173 View *firstResponder = $(self, firstResponder, event);
174 if (firstResponder) {
175
176 switch (event->type) {
177 case SDL_MOUSEMOTION:
178 break;
179 default: {
180 String *desc = $((Object *) firstResponder, description);
181 MVC_LogMessage(SDL_LOG_PRIORITY_DEBUG, "%d -> %s\n", event->type, desc->chars);
182 release(desc);
183 }
184 }
185
187 }
188
189 if (event->type == SDL_KEYUP) {
190 if (event->key.keysym.sym == SDLK_d) {
191 if (event->key.keysym.mod & KMOD_CTRL) {
192 $(self, toggleDebugger);
193 }
194 }
195 }
196 }
197}
198
203static void setRenderer(WindowController *self, Renderer *renderer) {
204
205 if (self->renderer != renderer || self->renderer == NULL) {
206
207 release(self->renderer);
208
209 if (renderer) {
210 self->renderer = retain(renderer);
211 } else {
212 self->renderer = $(alloc(Renderer), init);
213 }
214
216 }
217}
218
223static void setSoundStage(WindowController *self, SoundStage *soundStage) {
224
225 if (self->soundStage != soundStage || self->soundStage == NULL) {
226
227 release(self->soundStage);
228
229 if (soundStage) {
230 self->soundStage = retain(soundStage);
231 } else {
232 self->soundStage = $(alloc(SoundStage), init);
233 }
234 }
235}
236
241static void setTheme(WindowController *self, Theme *theme) {
242
243 if (self->theme != theme || self->theme == NULL) {
244
245 release(self->theme);
246
247 if (theme) {
248 self->theme = retain(theme);
249 } else {
250 self->theme = $(alloc(Theme), init);
251 }
252 }
253
255}
256
261static void setViewController(WindowController *self, ViewController *viewController) {
262
263 if (self->viewController != viewController || self->viewController == NULL) {
264
265 if (self->viewController) {
267 $(self->viewController->view, moveToWindow, NULL);
269 }
270
271 release(self->viewController);
272
273 if (viewController) {
274 self->viewController = retain(viewController);
275 } else {
276 self->viewController = $(alloc(ViewController), init);
277 }
278
280
282 $(self->viewController->view, moveToWindow, self->window);
284 }
285}
286
291static void setWindow(WindowController *self, SDL_Window *window) {
292
293 self->window = window;
294 assert(self->window);
295
296 const Uint32 flags = SDL_GetWindowFlags(self->window);
297 assert(flags & SDL_WINDOW_OPENGL);
298
299 SDL_SetWindowData(self->window, "windowController", self);
300 assert(SDL_GetWindowData(self->window, "windowController") == self);
301
302 if (self->viewController) {
303 $(self->viewController->view, moveToWindow, self->window);
304 }
305
306 if (self->debugViewController) {
308 }
309}
310
316
317 if (self->debugViewController == NULL) {
318
320 assert(self->debugViewController);
321
322 ViewController *debugViewController = (ViewController *) self->debugViewController;
323
324 $(debugViewController, loadView);
325 $(debugViewController, viewWillAppear);
326 $(debugViewController->view, moveToWindow, self->window);
327 $(debugViewController, viewDidAppear);
328
329 if (self->viewController) {
330 $(self->viewController->view, addClassName, "debug");
331 }
332 } else {
333 self->debugViewController = release(self->debugViewController);
334
335 if (self->viewController) {
336 $(self->viewController->view, removeClassName, "debug");
337 }
338 }
339}
340
345static WindowController *windowController(SDL_Window *window) {
346
347 assert(window);
348
349 return SDL_GetWindowData(window, "windowController");
350}
351
352#pragma mark - Class lifecycle
353
357static void initialize(Class *clazz) {
358
359 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
360
361 ((WindowControllerInterface *) clazz->interface)->debug = debug;
362 ((WindowControllerInterface *) clazz->interface)->eventTarget = eventTarget;
363 ((WindowControllerInterface *) clazz->interface)->firstResponder = firstResponder;
364 ((WindowControllerInterface *) clazz->interface)->initWithWindow = initWithWindow;
365 ((WindowControllerInterface *) clazz->interface)->render = render;
366 ((WindowControllerInterface *) clazz->interface)->respondToEvent = respondToEvent;
367 ((WindowControllerInterface *) clazz->interface)->setRenderer = setRenderer;
368 ((WindowControllerInterface *) clazz->interface)->setSoundStage = setSoundStage;
369 ((WindowControllerInterface *) clazz->interface)->setTheme = setTheme;
370 ((WindowControllerInterface *) clazz->interface)->setViewController = setViewController;
371 ((WindowControllerInterface *) clazz->interface)->setWindow = setWindow;
372 ((WindowControllerInterface *) clazz->interface)->toggleDebugger = toggleDebugger;
373 ((WindowControllerInterface *) clazz->interface)->windowController = windowController;
374}
375
380Class *_WindowController(void) {
381 static Class *clazz;
382 static Once once;
383
384 do_once(&once, {
385 clazz = _initialize(&(const ClassDef) {
386 .name = "WindowController",
387 .superclass = _Object(),
388 .instanceSize = sizeof(WindowController),
389 .interfaceOffset = offsetof(WindowController, interface),
390 .interfaceSize = sizeof(WindowControllerInterface),
392 });
393 });
394
395 return clazz;
396}
397
398#undef _Class
static String * description(const Object *self)
Definition: Label.c:47
View logging facilities via SDL_Log.
#define MVC_LogMessage(priority, fmt,...)
Definition: Log.h:40
Uint32 MVC_NOTIFICATION_EVENT
Definition: Notification.c:30
#define MakePoint(x, y)
Creates an SDL_Point with the given coordinates.
Definition: Types.h:69
static void dealloc(Object *self)
static void initialize(Class *clazz)
A WindowController manages a ViewController and its descendants within an SDL_Window.
CollectionView * init(CollectionView *self, const SDL_Rect *frame)
Initializes this CollectionView with the specified frame and style.
The DebugViewController type.
void debug(DebugViewController *self, const View *view, Renderer *renderer)
Debugs the given View.
ViewController viewController
The superclass.
void renderDeviceDidReset(Font *self)
This method should be invoked when the render context is invalidated.
Definition: Font.c:247
The Notification type.
Definition: Notification.h:34
The Renderer is responsible for rasterizing the View hierarchy of a WindowController.
Definition: Renderer.h:50
void endFrame(Renderer *self)
Definition: Renderer.c:212
void renderDeviceWillReset(Renderer *self)
This method is invoked when the render device will become reset.
Definition: Renderer.c:252
void beginFrame(Renderer *self)
Sets up OpenGL state.
Definition: Renderer.c:39
The SoundStage type.
Definition: SoundStage.h:40
The Theme type.
Definition: Theme.h:51
Theme * theme(SDL_Window *window)
Definition: Theme.c:143
A ViewController manages a View and its descendants.
View * view
The main view.
void loadView(ViewController *self)
Loads this ViewController's View.
void loadViewIfNeeded(ViewController *self)
Loads this ViewController's View if it is not already loaded.
void viewWillAppear(ViewController *self)
This method is invoked before this ViewController's View is added to the View hierarchy.
void viewDidDisappear(ViewController *self)
This method is invoked after this ViewController's View is removed to the View hierarchy.
void viewWillDisappear(ViewController *self)
This method is invoked before this ViewController's View is removed from the View hierarchy.
void handleNotification(ViewController *self, const Notification *notification)
Handles a broadcast notification.
void viewDidAppear(ViewController *self)
This method is invoked after this ViewController's View is added to the View hierarchy.
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition: View.h:133
void updateBindings(View *self)
Updates data bindings, prompting the appropriate layout changes.
void applyThemeIfNeeded(View *self, const Theme *theme)
Recursively applies the Theme to this View and its subviews.
Definition: View.c:269
void invalidateStyle(View *self)
Invalidates the computed Style for this View and its descendants.
Definition: View.c:822
View * hitTest(const View *self, const SDL_Point *point)
Performs a hit test against this View and its descendants for the given point.
Definition: View.c:743
View * firstResponder(SDL_Window *window)
Definition: View.c:712
void addClassName(View *self, const char *className)
Adds the given class name to this View.
Definition: View.c:120
void * draw(View *self, Renderer *renderer)
Draws this View.
Definition: View.c:572
void respondToEvent(View *self, const SDL_Event *event)
Responds to the specified event.
Definition: Control.c:213
void layoutIfNeeded(View *self)
Recursively updates the layout of this View and its subviews.
Definition: View.c:891
void moveToWindow(View *self, SDL_Window *window)
Moves this View to the View hierarchy of the given window.
Definition: View.c:1073
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
Definition: Control.c:131
void removeClassName(View *self, const char *className)
Removes the given class name to this View.
Definition: View.c:1158
A WindowController manages a ViewController and its descendants within an SDL_Window.
ViewController * viewController
The ViewController.
void toggleDebugger(WindowController *self)
Toggles the debugger tools.
void setWindow(WindowController *self, SDL_Window *window)
Sets this WindowController's window.
Theme * theme
The Theme.
WindowController * windowController(SDL_Window *window)
void setRenderer(WindowController *self, Renderer *renderer)
Sets this WindowController's Renderer.
Class * _WindowController(void)
The WindowController archetype.
SDL_Window * window
The window.
WindowController * initWithWindow(WindowController *self, SDL_Window *window)
Initializes this WindowController with the given window.
Renderer * renderer
The Renderer.
View * eventTarget(const WindowController *self, const SDL_Event *event)
void setSoundStage(WindowController *self, SoundStage *soundStage)
Sets this WindowController's SoundStage.
DebugViewController * debugViewController
The DebugViewController.
void render(WindowController *self)
Renders the ViewController's View.
SoundStage * soundStage
The SoundStage.
void setViewController(WindowController *self, ViewController *viewController)
Sets this WindowController's ViewController.
void setTheme(WindowController *self, Theme *theme)
Sets this WindowController's Theme.