353 {
354
355 bool didEdit = false, didComplete = false, didCaptureEvent = false;
356
358
360
361 const size_t position = this->position;
362
363 if (this->isEditable) {
364 if (event->type == SDL_EVENT_TEXT_INPUT) {
366 if (this->position == this->attributedText->length) {
367 $(this->attributedText, appendCharacters, event->text.text);
368 } else {
369 $(this->attributedText, insertCharactersAtIndex, event->text.text, this->position);
370 }
371 this->position += strlen(event->text.text);
372 didEdit = true;
373 didCaptureEvent = true;
374 }
375 } else if (event->type == SDL_EVENT_KEY_DOWN) {
376 didCaptureEvent = true;
377
378 const char *chars = this->attributedText->chars;
379 const size_t len = this->attributedText->length;
380
383
384 switch (event->key.key) {
385
386 case SDLK_TAB:
387 case SDLK_KP_TAB:
389 didEdit = didComplete = true;
390 } else {
392 }
393 break;
394
395 case SDLK_ESCAPE:
396 case SDLK_KP_ENTER:
397 case SDLK_RETURN:
399 break;
400
401 case SDLK_BACKSPACE:
402 case SDLK_KP_BACKSPACE:
403 if (this->position > 0) {
405 const Range range = { .location = this->position - n, .length = n };
406 $(this->attributedText, deleteCharactersInRange, range);
407 this->position -= n;
408 didEdit = true;
409 }
410 break;
411
412 case SDLK_DELETE:
413 if (this->position < len) {
414 const Range range = { .location = this->position, .length =
unitLengthAt(chars, len, this->position,
icons) };
415 $(this->attributedText, deleteCharactersInRange, range);
416 didEdit = true;
417 }
418 break;
419
420 case SDLK_LEFT:
421 if (SDL_GetModState() & SDL_KMOD_CTRL) {
422 while (this->position > 0 && chars[this->position] == ' ') {
424 }
425 while (this->position > 0 && chars[this->position] != ' ') {
427 }
428 } else {
430 }
431 break;
432
433 case SDLK_RIGHT:
434 if (SDL_GetModState() & SDL_KMOD_CTRL) {
435 while (this->position < len && chars[this->position] == ' ') {
437 }
438 while (this->position < len && chars[this->position] != ' ') {
440 }
441 if (this->position < len) {
443 }
444 } else {
446 }
447 break;
448
449 case SDLK_HOME:
450 this->position = 0;
451 break;
452
453 case SDLK_END:
454 this->position = len;
455 break;
456
457 case SDLK_A:
458 if (SDL_GetModState() & SDL_KMOD_CTRL) {
459 this->position = 0;
460 }
461 break;
462 case SDLK_E:
463 if (SDL_GetModState() & SDL_KMOD_CTRL) {
464 this->position = len;
465 }
466 break;
467
468 case SDLK_V:
469 if ((SDL_GetModState() & (SDL_KMOD_CTRL | SDL_KMOD_GUI)) && SDL_HasClipboardText()) {
470 char *text = SDL_GetClipboardText();
471 if (text != NULL) {
472 if (*text != '\0') {
473 if (this->position == len) {
474 $(this->attributedText, appendCharacters, text);
475 } else {
476 $(this->attributedText, insertCharactersAtIndex, text, this->position);
477 }
478 this->position += strlen(text);
479 didEdit = true;
480 }
481 SDL_free(text);
482 }
483 }
484 break;
485 }
486 }
487
488 if (!didComplete && (didEdit || this->position != position)) {
489 this->completions = release(this->completions);
490 }
491
492 if (didEdit) {
494 if (this->delegate.didEdit) {
495 this->delegate.didEdit(this);
496 }
497
499 }
500 }
501
502 if (didCaptureEvent) {
503 return true;
504 }
505
507}
static bool isFocused(const Control *self)
static bool autocomplete(TextView *self, int step, const ImageAtlas *icons)
Auto-completes the text before the cursor.
static size_t unitLengthBefore(const char *chars, size_t len, size_t position, const ImageAtlas *icons)
Returns the length in bytes of the cursor unit ending at position.
static bool captureEvent(Control *self, const SDL_Event *event)
static void resignKeyResponder(View *self)
static Theme * theme(SDL_Window *window)
@ ViewEventChange
A Control's input value has changed.
static void setNeedsLayout(View *self)
static void emitViewEvent(View *self, ViewEvent code, ident data)
Controls are Views which capture and respond to events.
Packs many Images into one sheet, backed by one Texture.