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

Go to the source code of this file.

Macros

#define _Class   _TextView
 

Functions

Class * _TextView (void)
 
static void applyStyle (View *self, const Style *style)
 
static void awakeWithDictionary (View *self, const Dictionary *dictionary)
 
static void becomeKeyResponder (View *self)
 
static bool captureEvent (Control *self, const SDL_Event *event)
 
static void dealloc (Object *self)
 
static Viewinit (View *self)
 
static void initialize (Class *clazz)
 
static TextViewinitWithFrame (TextView *self, const SDL_Rect *frame)
 
static void layoutSubviews (View *self)
 
static void render (View *self, Renderer *renderer)
 
static void resignKeyResponder (View *self)
 
static void setAttributedText (TextView *self, const char *attributedText)
 
static void setDefaultText (TextView *self, const char *defaultText)
 
static void stateDidChange (Control *self)
 

Macro Definition Documentation

◆ _Class

#define _Class   _TextView

Definition at line 33 of file TextView.c.

Function Documentation

◆ _TextView()

Class * _TextView ( void  )

Definition at line 444 of file TextView.c.

444 {
445 static Class *clazz;
446 static Once once;
447
448 do_once(&once, {
449 clazz = _initialize(&(const ClassDef) {
450 .name = "TextView",
451 .superclass = _Control(),
452 .instanceSize = sizeof(TextView),
453 .interfaceOffset = offsetof(TextView, interface),
454 .interfaceSize = sizeof(TextViewInterface),
456 });
457 });
458
459 return clazz;
460}
Class * _Control(void)
Definition Control.c:391
static void initialize(Class *clazz)
Definition TextView.c:420
A Control for presenting and capturing user-provied Text.
Definition TextView.h:79

◆ applyStyle()

static void applyStyle ( View self,
const Style style 
)
static

Definition at line 61 of file TextView.c.

61 {
62
63 super(View, self, applyStyle, style);
64
65 TextView *this = (TextView *) self;
66
67 const Inlet inlets[] = MakeInlets(
68 MakeInlet("editable", InletTypeBool, &this->isEditable, NULL)
69 );
70
71 $(self, bind, inlets, style->attributes);
72}
static void applyStyle(View *self, const Style *style)
Definition TextView.c:61
@ InletTypeBool
Definition View+JSON.h:46
#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
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:155
Dictionary * attributes
Definition Style.h:59
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:134

◆ awakeWithDictionary()

static void awakeWithDictionary ( View self,
const Dictionary *  dictionary 
)
static
See also
View::awakeWithDictionary(View *, const Dictionary *)

Definition at line 77 of file TextView.c.

77 {
78
79 super(View, self, awakeWithDictionary, dictionary);
80
81 TextView *this = (TextView *) self;
82
83 const Inlet inlets[] = MakeInlets(
84 MakeInlet("defaultText", InletTypeCharacters, &this->defaultText, NULL)
85 );
86
87 $(self, bind, inlets, dictionary);
88}
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition TextView.c:77
@ InletTypeCharacters
Definition View+JSON.h:52

◆ becomeKeyResponder()

static void becomeKeyResponder ( View self)
static
See also
View::becomeKeyResponder

Definition at line 93 of file TextView.c.

93 {
94
95 super(View, self, becomeKeyResponder);
96
97 SDL_StartTextInput(self->window);
98}
static void becomeKeyResponder(View *self)
Definition TextView.c:93
SDL_Window * window
The window.
Definition View.h:277

◆ captureEvent()

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

Definition at line 188 of file TextView.c.

188 {
189
190 bool didEdit = false, didCaptureEvent = false;
191
192 View *view = (View *) self;
193
194 TextView *this = (TextView *) self;
195
196 if (this->isEditable) {
197 if (event->type == SDL_EVENT_TEXT_INPUT) {
198 if ($(self, isFocused)) {
199 if (this->position == this->attributedText->length) {
200 $(this->attributedText, appendCharacters, event->text.text);
201 } else {
202 $(this->attributedText, insertCharactersAtIndex, event->text.text, this->position);
203 }
204 this->position += strlen(event->text.text);
205 didEdit = true;
206 didCaptureEvent = true;
207 }
208 } else if (event->type == SDL_EVENT_KEY_DOWN) {
209 didCaptureEvent = true;
210
211 const char *chars = this->attributedText->chars;
212 const size_t len = this->attributedText->length;
213
214 switch (event->key.key) {
215
216 case SDLK_ESCAPE:
217 case SDLK_KP_ENTER:
218 case SDLK_RETURN:
219 case SDLK_TAB:
220 case SDLK_KP_TAB:
221 $(view, resignKeyResponder);
222 break;
223
224 case SDLK_BACKSPACE:
225 case SDLK_KP_BACKSPACE:
226 if (this->position > 0) {
227 const Range range = { .location = this->position - 1, .length = 1 };
228 $(this->attributedText, deleteCharactersInRange, range);
229 this->position--;
230 didEdit = true;
231 }
232 break;
233
234 case SDLK_DELETE:
235 if (this->position < len) {
236 const Range range = { .location = this->position, .length = 1 };
237 $(this->attributedText, deleteCharactersInRange, range);
238 didEdit = true;
239 }
240 break;
241
242 case SDLK_LEFT:
243 if (SDL_GetModState() & SDL_KMOD_CTRL) {
244 while (this->position > 0 && chars[this->position] == ' ') {
245 this->position--;
246 }
247 while (this->position > 0 && chars[this->position] != ' ') {
248 this->position--;
249 }
250 } else if (this->position > 0) {
251 this->position--;
252 }
253 break;
254
255 case SDLK_RIGHT:
256 if (SDL_GetModState() & SDL_KMOD_CTRL) {
257 while (this->position < len && chars[this->position] == ' ') {
258 this->position++;
259 }
260 while (this->position < len && chars[this->position] != ' ') {
261 this->position++;
262 }
263 if (this->position < len) {
264 this->position++;
265 }
266 } else if (this->position < len) {
267 this->position++;
268 }
269 break;
270
271 case SDLK_HOME:
272 this->position = 0;
273 break;
274
275 case SDLK_END:
276 this->position = len;
277 break;
278
279 case SDLK_A:
280 if (SDL_GetModState() & SDL_KMOD_CTRL) {
281 this->position = 0;
282 }
283 break;
284 case SDLK_E:
285 if (SDL_GetModState() & SDL_KMOD_CTRL) {
286 this->position = len;
287 }
288 break;
289
290 case SDLK_V:
291 if ((SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_GUI)) && SDL_HasClipboardText()) {
292 char *text = SDL_GetClipboardText();
293 if (text != NULL) {
294 if (*text != '\0') {
295 if (this->position == len) {
296 $(this->attributedText, appendCharacters, text);
297 } else {
298 $(this->attributedText, insertCharactersAtIndex, text, this->position);
299 }
300 this->position += strlen(text);
301 didEdit = true;
302 }
303 SDL_free(text);
304 }
305 }
306 break;
307 }
308 }
309
310 if (didEdit) {
311 self->view.needsLayout = true;
312 if (this->delegate.didEdit) {
313 this->delegate.didEdit(this);
314 }
315
316 $((View *) self, emitViewEvent, ViewEventChange, NULL);
317 }
318 }
319
320 if (didCaptureEvent) {
321 return true;
322 }
323
324 return super(Control, self, captureEvent, event);
325}
static bool isFocused(const Control *self)
Definition Control.c:319
static bool captureEvent(Control *self, const SDL_Event *event)
Definition TextView.c:188
static void resignKeyResponder(View *self)
Definition TextView.c:176
@ ViewEventChange
A Control's input value has changed.
Definition Types.h:110
static void emitViewEvent(View *self, ViewEvent code, ident data)
Definition View.c:736
Controls are Views which capture and respond to events.
Definition Control.h:83
View view
The superclass.
Definition Control.h:88
bool needsLayout
If true, this View will layout its subviews before it is drawn.
Definition View.h:222

◆ dealloc()

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

Definition at line 40 of file TextView.c.

40 {
41
42 TextView *this = (TextView *) self;
43
44 memset(&this->delegate, 0, sizeof(this->delegate));
45
46 free(this->defaultText);
47
48 release(this->text);
49
50 release(this->attributedText);
51
52 super(Object, self, dealloc);
53}
static void dealloc(Object *self)
Definition TextView.c:40

◆ init()

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

Definition at line 103 of file TextView.c.

103 {
104 return (View *) $((TextView *) self, initWithFrame, NULL);
105}
static TextView * initWithFrame(TextView *self, const SDL_Rect *frame)
Definition TextView.c:359

◆ initialize()

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

Definition at line 420 of file TextView.c.

420 {
421
422 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
423
424 ((ViewInterface *) clazz->interface)->applyStyle = applyStyle;
425 ((ViewInterface *) clazz->interface)->becomeKeyResponder = becomeKeyResponder;
426 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
427 ((ViewInterface *) clazz->interface)->init = init;
428 ((ViewInterface *) clazz->interface)->layoutSubviews = layoutSubviews;
429 ((ViewInterface *) clazz->interface)->render = render;
430 ((ViewInterface *) clazz->interface)->resignKeyResponder = resignKeyResponder;
431
432 ((ControlInterface *) clazz->interface)->captureEvent = captureEvent;
433 ((ControlInterface *) clazz->interface)->stateDidChange = stateDidChange;
434
435 ((TextViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
436 ((TextViewInterface *) clazz->interface)->setAttributedText = setAttributedText;
437 ((TextViewInterface *) clazz->interface)->setDefaultText = setDefaultText;
438}
static void setAttributedText(TextView *self, const char *attributedText)
Definition TextView.c:383
static void stateDidChange(Control *self)
Definition TextView.c:330
static View * init(View *self)
Definition TextView.c:103
static void setDefaultText(TextView *self, const char *defaultText)
Definition TextView.c:399
static void render(View *self, Renderer *renderer)
Definition TextView.c:142
static void layoutSubviews(View *self)
Definition TextView.c:110
void setAttributedText(TextView *self, const char *attributedText)
Definition TextView.c:383
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.
Definition Box.c:74

◆ initWithFrame()

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

Definition at line 359 of file TextView.c.

359 {
360
361 self = (TextView *) super(Control, self, initWithFrame, frame);
362 if (self) {
363 self->attributedText = $$(String, string);
364 assert(self->attributedText);
365
366 self->isEditable = true;
367
368 self->text = $(alloc(Text), initWithText, NULL, NULL);
369 assert(self->text);
370
371 $((View *) self, addSubview, (View *) self->text);
372
373 self->control.view.clipsSubviews = true;
374 }
375
376 return self;
377}
static Label * initWithText(Label *self, const char *text, Font *font)
Definition Label.c:97
static void addSubview(View *self, View *subview)
Definition PageView.c:49
Text rendered with TrueType fonts.
Definition Text.h:69
bool isEditable
True if this TextView supports editing, false otherwise.
Definition TextView.h:110
String * attributedText
The user-provided text.
Definition TextView.h:95
Text * text
The text.
Definition TextView.h:120
bool clipsSubviews
If true, subviews will be clipped to this View's frame.
Definition View.h:181

◆ layoutSubviews()

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

Definition at line 110 of file TextView.c.

110 {
111
112 TextView *this = (TextView *) self;
113
114 const char *text = this->attributedText->chars;
115
116 if (text == NULL || strlen(text) == 0) {
117 if ($((Control *) this, isFocused) == false) {
118 text = this->defaultText;
119 }
120 }
121
122 if (text == NULL) {
123 $(this->text, setText, NULL);
124 } else {
125 if (this->text->text) {
126 if (strcmp(text, this->text->text)) {
127 $(this->text, setText, text);
128 }
129 } else {
130 $(this->text, setText, text);
131 }
132 }
133
134 $((View *) this->text, layoutIfNeeded);
135
136 super(View, self, layoutSubviews);
137}
static void setText(Text *self, const char *text)
Definition Text.c:532
static void layoutIfNeeded(View *self)
Definition View.c:1115

◆ render()

static void render ( View self,
Renderer renderer 
)
static
See also
View::render(View *, Renderer *)

Definition at line 142 of file TextView.c.

142 {
143
144 super(View, self, render, renderer);
145
146 TextView *this = (TextView *) self;
147
148 if ($((Control *) this, isFocused)) {
149 const char *text = this->text->text ?: "";
150
151 int w, h;
152 if (this->position == strlen(text)) {
153 $(this->text->font, sizeCharacters, text, &w, &h);
154 } else {
155 char *chars = calloc(this->position + 1, sizeof(char));
156 strncpy(chars, text, this->position);
157
158 $(this->text->font, sizeCharacters, chars, &w, &h);
159 free(chars);
160 }
161
162 SDL_Rect frame = $((View *) this->text, renderFrame);
163
164 const SDL_Point points[] = {
165 { frame.x + w, frame.y },
166 { frame.x + w, frame.y + h }
167 };
168
169 $(renderer, drawLine, points, &Colors.White);
170 }
171}
static void sizeCharacters(const Font *self, const char *chars, int *w, int *h)
Definition Font.c:273
static void drawLine(const Renderer *self, const SDL_Point *points, const SDL_Color *color)
Definition Renderer.c:114
static SDL_Rect renderFrame(const View *self)
Definition View.c:1455
W3C Color constants.
Definition Colors.h:37
SDL_Color White
Definition Colors.h:185

◆ resignKeyResponder()

static void resignKeyResponder ( View self)
static
See also
View::resignKeyResponder

Definition at line 176 of file TextView.c.

176 {
177
178 super(View, self, resignKeyResponder);
179
180 SDL_StopTextInput(self->window);
181}

◆ setAttributedText()

static void setAttributedText ( TextView self,
const char *  attributedText 
)
static

Definition at line 383 of file TextView.c.

383 {
384
385 if (strcmp(self->attributedText->chars ?: "", attributedText ?: "")) {
386
387 $(self->attributedText, setCharacters, attributedText);
388
389 self->position = self->attributedText->length;
390
391 self->control.view.needsLayout = true;
392 }
393}
size_t position
Definition TextView.h:115
Control control
The superclass.
Definition TextView.h:84

◆ setDefaultText()

static void setDefaultText ( TextView self,
const char *  defaultText 
)
static

Definition at line 399 of file TextView.c.

399 {
400
401 if (strcmp(self->defaultText ?: "", defaultText ?: "")) {
402
403 free(self->defaultText);
404
405 if (defaultText) {
406 self->defaultText = strdup(defaultText);
407 } else {
408 self->defaultText = NULL;
409 }
410
411 self->control.view.needsLayout = true;
412 }
413}
char * defaultText
The default text, displayed when no user-provided text is available.
Definition TextView.h:100

◆ stateDidChange()

static void stateDidChange ( Control self)
static

Definition at line 330 of file TextView.c.

330 {
331
332 TextView *this = (TextView *) self;
333
334 if ($(self, isFocused)) {
335
336 SDL_StartTextInput(self->view.window);
337
338 if (this->delegate.didBeginEditing) {
339 this->delegate.didBeginEditing(this);
340 }
341 } else {
342
343 SDL_StopTextInput(self->view.window);
344
345 if (this->delegate.didEndEditing) {
346 this->delegate.didEndEditing(this);
347 }
348 }
349
350 super(Control, self, stateDidChange);
351}