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

Go to the source code of this file.

Macros

#define _Class   _WindowController
 

Functions

Class * _WindowController (void)
 
static void dealloc (Object *self)
 
static void debug (WindowController *self)
 
static void enumerateKeyResponders (View *view, ident data)
 ViewEnumerator for finding the next key responder.
 
static void initialize (Class *clazz)
 
static WindowController * initWithDevice (WindowController *self, RenderDevice *device)
 
static View * keyResponder (const WindowController *self)
 
static Array * keyResponders (const WindowController *self)
 
static void mouseMotion_enumerate (View *view, ident data)
 ViewEnumerator for SDL_EVENT_MOUSE_MOTION.
 
static View * nextKeyResponder (const WindowController *self, View *keyResponder)
 
static View * previousKeyResponder (const WindowController *self, View *keyResponder)
 
static void render (WindowController *self)
 
static void renderTo (WindowController *self, CommandBuffer *commands, Framebuffer *framebuffer)
 
static void respondToEvent (WindowController *self, const SDL_Event *event)
 
static void setTheme (WindowController *self, Theme *theme)
 
static void setViewController (WindowController *self, ViewController *viewController)
 
static void setWindow (WindowController *self, SDL_Window *window)
 
static void toggleDebugger (WindowController *self)
 
static View * touchResponder (const WindowController *self)
 
static View * touchTarget (const WindowController *self, const SDL_Event *event)
 
static WindowController * windowController (SDL_Window *window)
 

Macro Definition Documentation

◆ _Class

#define _Class   _WindowController

Definition at line 32 of file WindowController.c.

Function Documentation

◆ _WindowController()

Class * _WindowController ( void  )

Definition at line 578 of file WindowController.c.

578 {
579 static Class *clazz;
580 static Once once;
581
582 do_once(&once, {
583 clazz = _initialize(&(const ClassDef) {
584 .name = "WindowController",
585 .superclass = _Object(),
586 .instanceSize = sizeof(WindowController),
587 .interfaceSize = sizeof(WindowControllerInterface),
588 .initialize = initialize,
589 });
590 });
591
592 return clazz;
593}
static void initialize(Class *clazz)
A WindowController manages a ViewController and its descendants within an SDL_Window.

◆ dealloc()

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

Definition at line 59 of file WindowController.c.

59 {
60
61 WindowController *this = (WindowController *) self;
62
63 release(this->debugViewController);
64 release(this->renderer);
65 release(this->theme);
66 release(this->viewController);
67
68 super(Object, self, dealloc);
69}
static Theme * theme(SDL_Window *window)
Definition Theme.c:295
static void dealloc(Object *self)

◆ debug()

static void debug ( WindowController *  self)
static

Definition at line 77 of file WindowController.c.

77 {
78
79 if (self->viewController && self->debugViewController) {
80
81 ViewController *debugViewController = (ViewController *) self->debugViewController;
82
83 float mx, my;
84 SDL_GetMouseState(&mx, &my);
85 SDL_Point point = MakePoint(mx, my);
86
87 View *view = $(self->viewController->view, hitTest, &point);
88 if (view) {
89 $(self->debugViewController, debug, view, self->renderer);
90 }
91
92 $(debugViewController->view, applyThemeIfNeeded, self->theme);
93 $(debugViewController->view, layoutIfNeeded);
94
96
97 $(debugViewController->view, draw, self->renderer);
98 }
99}
static void layoutIfNeeded(View *self)
Definition View.c:1174
static void draw(View *self, Renderer *renderer)
Definition View.c:778
void MVC_InvalidateRenderFrames(void)
Invalidates every View's cached View::renderFrame and View::clippingFrame.
Definition View.c:46
static View * hitTest(const View *self, const SDL_Point *point)
Definition View.c:1012
static void applyThemeIfNeeded(View *self, const Theme *theme)
Definition View.c:369
static void debug(WindowController *self)
A ViewController manages a View and its descendants.
View * view
The main view.
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:225
ViewController * viewController
The ViewController.
Theme * theme
The Theme.
Renderer * renderer
The Renderer.
DebugViewController * debugViewController
The DebugViewController.

◆ enumerateKeyResponders()

static void enumerateKeyResponders ( View *  view,
ident  data 
)
static

ViewEnumerator for finding the next key responder.

Definition at line 112 of file WindowController.c.

112 {
113
114 if ($(view, acceptsKeyResponder) && $(view, isVisible)) {
115 $((Array *) data, addObject, view);
116 }
117}
static bool acceptsKeyResponder(const View *self)
Definition Control.c:69
static bool isVisible(const View *self)
Definition View.c:1152

◆ initialize()

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

Definition at line 552 of file WindowController.c.

552 {
553
554 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
555
556 ((WindowControllerInterface *) clazz->interface)->debug = debug;
557 ((WindowControllerInterface *) clazz->interface)->keyResponder = keyResponder;
558 ((WindowControllerInterface *) clazz->interface)->keyResponders = keyResponders;
559 ((WindowControllerInterface *) clazz->interface)->initWithDevice = initWithDevice;
560 ((WindowControllerInterface *) clazz->interface)->nextKeyResponder = nextKeyResponder;
561 ((WindowControllerInterface *) clazz->interface)->previousKeyResponder = previousKeyResponder;
562 ((WindowControllerInterface *) clazz->interface)->render = render;
563 ((WindowControllerInterface *) clazz->interface)->renderTo = renderTo;
564 ((WindowControllerInterface *) clazz->interface)->respondToEvent = respondToEvent;
565 ((WindowControllerInterface *) clazz->interface)->setTheme = setTheme;
566 ((WindowControllerInterface *) clazz->interface)->setViewController = setViewController;
567 ((WindowControllerInterface *) clazz->interface)->setWindow = setWindow;
568 ((WindowControllerInterface *) clazz->interface)->toggleDebugger = toggleDebugger;
569 ((WindowControllerInterface *) clazz->interface)->touchResponder = touchResponder;
570 ((WindowControllerInterface *) clazz->interface)->touchTarget = touchTarget;
571 ((WindowControllerInterface *) clazz->interface)->windowController = windowController;
572}
static View * touchTarget(const WindowController *self, const SDL_Event *event)
static WindowController * initWithDevice(WindowController *self, RenderDevice *device)
static Array * keyResponders(const WindowController *self)
static void toggleDebugger(WindowController *self)
static View * keyResponder(const WindowController *self)
static void setViewController(WindowController *self, ViewController *viewController)
static void setWindow(WindowController *self, SDL_Window *window)
static View * touchResponder(const WindowController *self)
static void render(WindowController *self)
static View * previousKeyResponder(const WindowController *self, View *keyResponder)
static void setTheme(WindowController *self, Theme *theme)
static WindowController * windowController(SDL_Window *window)
static void renderTo(WindowController *self, CommandBuffer *commands, Framebuffer *framebuffer)
static View * nextKeyResponder(const WindowController *self, View *keyResponder)
static void respondToEvent(WindowController *self, const SDL_Event *event)
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
Definition Control.c:181
View * nextKeyResponder(const WindowController *self, View *keyResponder)
Finds the next available keyResponder from the given keyResponder.

◆ initWithDevice()

static WindowController * initWithDevice ( WindowController *  self,
RenderDevice *  device 
)
static

Definition at line 135 of file WindowController.c.

135 {
136
137 assert(device);
138
139 self = (WindowController *) super(Object, self, init);
140 if (self) {
141 self->renderer = $(alloc(Renderer), initWithDevice, device);
142 assert(self->renderer);
143
144 $(self, setWindow, device->window);
145 $(self, setViewController, NULL);
146 $(self, setTheme, NULL);
147
150 $(self->theme, renderDeviceDidReset);
151 }
152
153 return self;
154}
static View * init(View *self)
Definition Box.c:67
static void renderDeviceDidReset(Renderer *self)
Definition Renderer.c:705
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:75

◆ keyResponder()

static View * keyResponder ( const WindowController *  self)
static

Definition at line 105 of file WindowController.c.

105 {
106 return SDL_GetPointerProperty(SDL_GetWindowProperties(self->window), "keyResponder", NULL);
107}
SDL_Window * window
The window.

◆ keyResponders()

static Array * keyResponders ( const WindowController *  self)
static

Definition at line 123 of file WindowController.c.

123 {
124
125 Array *array = (Array *) $(alloc(Array), init);
127
128 return array;
129}
static void enumerateDescendants(const View *self, ViewEnumerator enumerator, ident data)
Definition View.c:866
static void enumerateKeyResponders(View *view, ident data)
ViewEnumerator for finding the next key responder.

◆ mouseMotion_enumerate()

static void mouseMotion_enumerate ( View *  view,
ident  data 
)
static

ViewEnumerator for SDL_EVENT_MOUSE_MOTION.

Definition at line 39 of file WindowController.c.

39 {
40
41 const SDL_MouseMotionEvent *event = data;
42 const SDL_Point a = MakePoint(event->x - event->xrel, event->y - event->yrel);
43 const SDL_Point b = MakePoint(event->x, event->y);
44
45 if ($(view, containsPoint, &a) && !$(view, containsPoint, &b)) {
46 $(view, emitViewEvent, ViewEventMouseLeave, NULL);
47 $(view, invalidateStyle);
48 } else if ($(view, containsPoint, &b) && !$(view, containsPoint, &a)) {
49 $(view, emitViewEvent, ViewEventMouseEnter, NULL);
50 $(view, invalidateStyle);
51 }
52}
@ ViewEventMouseEnter
The mouse has entered a View's frame.
Definition Types.h:90
@ ViewEventMouseLeave
The mouse has left a View's frame.
Definition Types.h:95
static void invalidateStyle(View *self)
Definition View.c:1092
static bool containsPoint(const View *self, const SDL_Point *point)
Definition View.c:647
static void emitViewEvent(View *self, ViewEvent code, ident data)
Definition View.c:794

◆ nextKeyResponder()

static View * nextKeyResponder ( const WindowController *  self,
View *  keyResponder 
)
static

Definition at line 196 of file WindowController.c.

196 {
197
198 View *next = NULL;
199
200 Array *array = $(self, keyResponders);
201
202 if (array->count) {
203 if (keyResponder && $(array, containsObject, keyResponder)) {
204 const ssize_t index = $(array, indexOfObject, keyResponder);
205 next = $(array, objectAtIndex, (index + 1) % array->count);
206 } else {
207 next = $(array, firstObject);
208 }
209 }
210
211 release(array);
212 return next;
213}

◆ previousKeyResponder()

static View * previousKeyResponder ( const WindowController *  self,
View *  keyResponder 
)
static

Definition at line 219 of file WindowController.c.

219 {
220
221 View *prev = NULL;
222
223 Array *array = $(self, keyResponders);
224
225 if (array->count) {
226 if (keyResponder && $(array, containsObject, keyResponder)) {
227 const ssize_t index = $(array, indexOfObject, keyResponder);
228 prev = $(array, objectAtIndex, (index + array->count - 1) % array->count);
229 } else {
230 prev = $(array, firstObject);
231 }
232 }
233
234 release(array);
235 return prev;
236}

◆ render()

static void render ( WindowController *  self)
static

Definition at line 160 of file WindowController.c.

160 {
161
162 assert(self->renderer);
163
164 const RenderDevice *device = self->renderer->device;
165 $(self, renderTo, device->commands, device->framebuffer);
166}
RenderDevice * device
The backing RenderDevice.
Definition Renderer.h:97

◆ renderTo()

static void renderTo ( WindowController *  self,
CommandBuffer *  commands,
Framebuffer *  framebuffer 
)
static

Definition at line 172 of file WindowController.c.

172 {
173
174 assert(self->renderer);
175 assert(commands);
176 assert(framebuffer);
177
178 $(self->renderer, beginFrameWith, commands, framebuffer);
179
182
184
185 $(self->viewController->view, draw, self->renderer);
186
187 $(self, debug);
188
189 $(self->renderer, endFrame);
190}
static void endFrame(Renderer *self)
Definition Renderer.c:555
static void beginFrameWith(Renderer *self, CommandBuffer *commands, Framebuffer *framebuffer)
Definition Renderer.c:109

◆ respondToEvent()

static void respondToEvent ( WindowController *  self,
const SDL_Event *  event 
)
static

Definition at line 248 of file WindowController.c.

248 {
249
250 if (event->type >= SDL_EVENT_WINDOW_FIRST && event->type <= SDL_EVENT_WINDOW_LAST) {
251 if (event->window.windowID != SDL_GetWindowID(self->window)) {
252 return;
253 }
254 }
255
256 SDL_SetPointerProperty(SDL_GetWindowProperties(self->window), "event", (ident) event);
257
258 switch (event->type) {
259 case SDL_EVENT_WINDOW_MOVED:
260 case SDL_EVENT_WINDOW_RESIZED:
261 case SDL_EVENT_WINDOW_MAXIMIZED:
262 case SDL_EVENT_WINDOW_RESTORED:
263 case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED:
264 $(self, setWindow, self->window);
265 break;
266 case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
267 case SDL_EVENT_WINDOW_DISPLAY_CHANGED:
268 case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: {
269 $(self, setWindow, self->window);
270
273 $(self->viewController->view, updateBindings, NULL);
275 $(self->theme, renderDeviceDidReset);
276
277 if (self->debugViewController) {
278 ViewController *debugViewController = (ViewController *) self->debugViewController;
279 $(debugViewController, renderDeviceWillReset);
280 $(debugViewController, renderDeviceDidReset);
281 $(debugViewController->view, updateBindings, NULL);
282 }
283 }
284 break;
285 case SDL_EVENT_WINDOW_DESTROYED:
286 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
290 break;
291 default:
292 break;
293 }
294
295 if (event->type == SDL_EVENT_MOUSE_MOTION) {
297 }
298
300
301 View *touchTarget = $(self, touchTarget, event);
302
303 switch (event->type) {
304 case SDL_EVENT_KEY_UP:
305 case SDL_EVENT_KEY_DOWN:
306 case SDL_EVENT_TEXT_INPUT:
307 if (keyResponder) {
308 $(keyResponder, respondToEvent, event);
309 } else {
310 $(self->viewController, respondToEvent, event);
311 }
312 break;
313 case SDL_EVENT_MOUSE_BUTTON_DOWN:
314 if (touchTarget) {
316 $(touchTarget, respondToEvent, event);
317 } else if (touchResponder) {
320 } else {
321 $(self->viewController, respondToEvent, event);
322 }
323
327 }
328 break;
329 case SDL_EVENT_MOUSE_BUTTON_UP:
330 if (touchResponder) {
332 } else if (touchTarget) {
333 $(touchTarget, respondToEvent, event);
334 } else {
335 $(self->viewController, respondToEvent, event);
336 }
337 break;
338 case SDL_EVENT_MOUSE_MOTION:
339 case SDL_EVENT_MOUSE_WHEEL:
340 if (touchResponder) {
342 } else if (touchTarget) {
343 $(touchTarget, respondToEvent, event);
344 } else {
345 $(self->viewController, respondToEvent, event);
346 }
347 break;
348 default:
349 $(self->viewController, respondToEvent, event);
350 break;
351 }
352
353 if (event->type == SDL_EVENT_KEY_DOWN) {
354 if (event->key.key == SDLK_TAB || event->key.key == SDLK_KP_TAB) {
355
356 View *current = $(self, keyResponder);
357
358 // An editing TextView that is still the key responder kept Tab to auto-complete
359 const bool consumed = current && current == keyResponder &&
360 $((Object *) current, isKindOfClass, _TextView()) &&
361 ((TextView *) current)->isEditable &&
362 !$((Control *) current, isDisabled);
363
364 if (!consumed) {
365 if (event->key.mod & SDL_KMOD_SHIFT) {
367 } else {
369 }
370
371 if (keyResponder) {
373 }
374 }
375 }
376
377 if (event->key.key == SDLK_D) {
378 if (event->key.mod & SDL_KMOD_CTRL) {
379 $(self, toggleDebugger);
380 }
381 }
382 }
383
384 SDL_SetPointerProperty(SDL_GetWindowProperties(self->window), "event", NULL);
385}
static void becomeKeyResponder(View *self)
Definition Control.c:132
static void resignKeyResponder(View *self)
Definition Control.c:265
static bool isDisabled(const Control *self)
Definition Control.c:334
static void updateBindings(View *self, ident data)
static void renderDeviceWillReset(ImageAtlas *self)
Definition ImageAtlas.c:281
Class * _TextView(void)
Definition TextView.c:629
static void enumerateVisible(View *self, ViewEnumerator enumerator, ident data)
Definition View.c:931
static void becomeTouchResponder(View *self)
Definition View.c:511
static void resignTouchResponder(View *self)
Definition View.c:1692
static void mouseMotion_enumerate(View *view, ident data)
ViewEnumerator for SDL_EVENT_MOUSE_MOTION.
Controls are Views which capture and respond to events.
Definition Control.h:83
A Control for presenting and capturing user-provied Text.
Definition TextView.h:93

◆ setTheme()

static void setTheme ( WindowController *  self,
Theme *  theme 
)
static

Definition at line 391 of file WindowController.c.

391 {
392
393 if (self->theme != theme || self->theme == NULL) {
394
395 release(self->theme);
396
397 if (theme) {
398 self->theme = retain(theme);
399 } else {
400 self->theme = $(alloc(Theme), init);
401 }
402 }
403
405}
The Theme type.
Definition Theme.h:54

◆ setViewController()

static void setViewController ( WindowController *  self,
ViewController *  viewController 
)
static

Definition at line 411 of file WindowController.c.

411 {
412
413 if (self->viewController != viewController || self->viewController == NULL) {
414
415 SDL_SetPointerProperty(SDL_GetWindowProperties(self->window), "keyResponder", NULL);
416
417 if (self->viewController) {
419 $(self->viewController->view, moveToWindow, NULL);
421 }
422
423 release(self->viewController);
424
425 if (viewController) {
426 self->viewController = retain(viewController);
427 } else {
428 self->viewController = $(alloc(ViewController), init);
429 }
430
432
434 $(self->viewController->view, moveToWindow, self->window);
436 }
437}
static void moveToWindow(View *self, SDL_Window *window)
Definition View.c:1351
static void viewWillAppear(ViewController *self)
static void viewWillDisappear(ViewController *self)
static void loadViewIfNeeded(ViewController *self)
static void viewDidAppear(ViewController *self)
static void viewDidDisappear(ViewController *self)

◆ setWindow()

static void setWindow ( WindowController *  self,
SDL_Window *  window 
)
static

Definition at line 443 of file WindowController.c.

443 {
444
445 assert(window);
446
447 self->window = window;
448
449 SDL_PropertiesID properties = SDL_GetWindowProperties(self->window);
450
451 SDL_SetPointerProperty(properties, "windowController", self);
452 SDL_SetPointerProperty(properties, "keyResponder", NULL);
453 SDL_SetPointerProperty(properties, "touchResponder", NULL);
454
455 if (self->renderer) {
456 $(self->renderer->device, setWindow, self->window);
457 }
458
459 if (self->viewController) {
460 $(self->viewController->view, moveToWindow, self->window);
461 }
462
463 if (self->debugViewController) {
465 }
466}
ViewController viewController
The superclass.

◆ toggleDebugger()

static void toggleDebugger ( WindowController *  self)
static

Definition at line 472 of file WindowController.c.

472 {
473
474 if (self->debugViewController == NULL) {
475
477 assert(self->debugViewController);
478
479 ViewController *debugViewController = (ViewController *) self->debugViewController;
480
481 $(debugViewController, loadView);
482 $(debugViewController, viewWillAppear);
483 $(debugViewController->view, moveToWindow, self->window);
484 $(debugViewController, viewDidAppear);
485
486 if (self->viewController) {
487 $(self->viewController->view, addClassName, "debug");
488 }
489 } else {
490 self->debugViewController = release(self->debugViewController);
491
492 if (self->viewController) {
493 $(self->viewController->view, removeClassName, "debug");
494 }
495 }
496}
static void loadView(ViewController *self)
static void removeClassName(View *self, const char *className)
Definition View.c:1445
static void addClassName(View *self, const char *className)
Definition View.c:183
The DebugViewController type.

◆ touchResponder()

static View * touchResponder ( const WindowController *  self)
static

Definition at line 502 of file WindowController.c.

502 {
503 return SDL_GetPointerProperty(SDL_GetWindowProperties(self->window), "touchResponder", NULL);
504}

◆ touchTarget()

static View * touchTarget ( const WindowController *  self,
const SDL_Event *  event 
)
static

Definition at line 510 of file WindowController.c.

510 {
511 SDL_Point point;
512
513 switch (event->type) {
514 case SDL_EVENT_MOUSE_BUTTON_DOWN:
515 point = MakePoint(event->button.x, event->button.y);
516 break;
517 case SDL_EVENT_MOUSE_BUTTON_UP:
518 point = MakePoint(event->button.x, event->button.y);
519 break;
520 case SDL_EVENT_MOUSE_MOTION:
521 point = MakePoint(event->motion.x, event->motion.y);
522 break;
523 case SDL_EVENT_MOUSE_WHEEL: {
524 float mx, my;
525 SDL_GetMouseState(&mx, &my);
526 point = MakePoint(mx, my);
527 break;
528 }
529 default:
530 return NULL;
531 }
532
533 return $(self->viewController->view, hitTest, &point);
534}

◆ windowController()

static WindowController * windowController ( SDL_Window *  window)
static

Definition at line 540 of file WindowController.c.

540 {
541
542 assert(window);
543
544 return SDL_GetPointerProperty(SDL_GetWindowProperties(window), "windowController", NULL);
545}