28#include <Objectively/String.h>
33#define _Class _TextView
44 memset(&this->delegate, 0,
sizeof(this->delegate));
46 free(this->defaultText);
50 release(this->attributedText);
87 $(self, bind, inlets, dictionary);
97 SDL_StartTextInput(self->
window);
114 const char *text = this->attributedText->chars;
116 if (text == NULL || strlen(text) == 0) {
118 text = this->defaultText;
125 if (this->text->text) {
126 if (strcmp(text, this->text->text)) {
149 const char *text = this->text->text ?:
"";
152 if (this->position == strlen(text)) {
155 char *chars = calloc(this->position + 1,
sizeof(
char));
156 strncpy(chars, text, this->position);
164 const SDL_Point points[] = {
165 { frame.x + w, frame.y },
166 { frame.x + w, frame.y + h }
180 SDL_StopTextInput(self->
window);
183#pragma mark - Control
190 bool didEdit =
false, didCaptureEvent =
false;
196 if (this->isEditable) {
197 if (event->type == SDL_EVENT_TEXT_INPUT) {
199 if (this->position == this->attributedText->length) {
200 $(this->attributedText, appendCharacters, event->text.text);
202 $(this->attributedText, insertCharactersAtIndex, event->text.text, this->position);
204 this->position += strlen(event->text.text);
206 didCaptureEvent =
true;
208 }
else if (event->type == SDL_EVENT_KEY_DOWN) {
209 didCaptureEvent =
true;
211 const char *chars = this->attributedText->chars;
212 const size_t len = this->attributedText->length;
214 switch (event->key.key) {
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);
235 if (this->position < len) {
236 const Range range = { .location = this->position, .length = 1 };
237 $(this->attributedText, deleteCharactersInRange, range);
243 if (SDL_GetModState() & SDL_KMOD_CTRL) {
244 while (this->position > 0 && chars[this->position] ==
' ') {
247 while (this->position > 0 && chars[this->position] !=
' ') {
250 }
else if (this->position > 0) {
256 if (SDL_GetModState() & SDL_KMOD_CTRL) {
257 while (this->position < len && chars[this->position] ==
' ') {
260 while (this->position < len && chars[this->position] !=
' ') {
263 if (this->position < len) {
266 }
else if (this->position < len) {
276 this->position = len;
280 if (SDL_GetModState() & SDL_KMOD_CTRL) {
285 if (SDL_GetModState() & SDL_KMOD_CTRL) {
286 this->position = len;
291 if ((SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_GUI)) && SDL_HasClipboardText()) {
292 char *text = SDL_GetClipboardText();
295 if (this->position == len) {
296 $(this->attributedText, appendCharacters, text);
298 $(this->attributedText, insertCharactersAtIndex, text, this->position);
300 this->position += strlen(text);
312 if (this->delegate.didEdit) {
313 this->delegate.didEdit(
this);
320 if (didCaptureEvent) {
338 if (this->delegate.didBeginEditing) {
339 this->delegate.didBeginEditing(
this);
345 if (this->delegate.didEndEditing) {
346 this->delegate.didEndEditing(
this);
353#pragma mark - TextView
385 if (strcmp(self->
attributedText->chars ?:
"", attributedText ?:
"")) {
401 if (strcmp(self->
defaultText ?:
"", defaultText ?:
"")) {
415#pragma mark - Class lifecycle
422 ((ObjectInterface *) clazz->interface)->dealloc =
dealloc;
424 ((ViewInterface *) clazz->interface)->applyStyle =
applyStyle;
427 ((ViewInterface *) clazz->interface)->init =
init;
429 ((ViewInterface *) clazz->interface)->render =
render;
432 ((ControlInterface *) clazz->interface)->captureEvent =
captureEvent;
433 ((ControlInterface *) clazz->interface)->stateDidChange =
stateDidChange;
435 ((TextViewInterface *) clazz->interface)->initWithFrame =
initWithFrame;
437 ((TextViewInterface *) clazz->interface)->setDefaultText =
setDefaultText;
449 clazz = _initialize(&(
const ClassDef) {
453 .interfaceOffset = offsetof(
TextView, interface),
454 .interfaceSize =
sizeof(TextViewInterface),
static bool isFocused(const Control *self)
static void sizeCharacters(const Font *self, const char *chars, int *w, int *h)
static Label * initWithText(Label *self, const char *text, Font *font)
static void addSubview(View *self, View *subview)
static void drawLine(const Renderer *self, const SDL_Point *points, const SDL_Color *color)
static void setText(Text *self, const char *text)
static void setAttributedText(TextView *self, const char *attributedText)
static void becomeKeyResponder(View *self)
static void stateDidChange(Control *self)
static View * init(View *self)
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
static TextView * initWithFrame(TextView *self, const SDL_Rect *frame)
static void applyStyle(View *self, const Style *style)
static bool captureEvent(Control *self, const SDL_Event *event)
static void setDefaultText(TextView *self, const char *defaultText)
static void render(View *self, Renderer *renderer)
static void dealloc(Object *self)
static void resignKeyResponder(View *self)
static void initialize(Class *clazz)
static void layoutSubviews(View *self)
A Control for presenting and capturing user-provied Text.
@ ViewEventChange
A Control's input value has changed.
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
static void layoutIfNeeded(View *self)
static SDL_Rect renderFrame(const View *self)
static void emitViewEvent(View *self, ViewEvent code, ident data)
Controls are Views which capture and respond to events.
Inlets enable inbound data binding of View attributes through JSON.
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Text rendered with TrueType fonts.
A Control for presenting and capturing user-provied Text.
bool isEditable
True if this TextView supports editing, false otherwise.
char * defaultText
The default text, displayed when no user-provided text is available.
String * attributedText
The user-provided text.
Control control
The superclass.
void setAttributedText(TextView *self, const char *attributedText)
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
bool clipsSubviews
If true, subviews will be clipped to this View's frame.
SDL_Window * window
The window.
bool needsLayout
If true, this View will layout its subviews before it is drawn.
layoutSubviews(View *self)
Performs layout for this View's immediate subviews.