/*
* Copyright (C) 2021 P. J. McDermott
*
* This file is part of Maze Fight
*
* Maze Fight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Maze Fight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Maze Fight. If not, see .
*/
#include
#include
#include
#include
#include "../tk.h"
#include "../util.h"
#include "style.h"
#include "widget.h"
struct mftk_text {
struct mftk_widget parent;
const char *allowed_chars;
int len;
int cur;
int y;
int w;
int h;
char *val;
char *curval;
TTF_Font *font;
int line_skip;
int ascent;
SDL_Texture *texture;
int padding;
int border;
int editable;
int blink_state;
Uint32 blink_timer;
int (*action)(void *, const char *);
int (*submit)(void *);
void *user_data;
};
static SDL_Color _mftk_text_color = {
.r = MFTK_COLOR_FORE_R, .g = MFTK_COLOR_FORE_G,
.b = MFTK_COLOR_FORE_B, .a = MFTK_COLOR_FORE_A
};
static void
_mftk_text_layout(struct mftk_widget *w __attribute__((__unused__)))
{
/* Size set in constructor */
}
static void
_mftk_text_focus(struct mftk_widget *w __attribute__((__unused__)))
{
struct mftk_text *t = (struct mftk_text *) w;
SDL_StartTextInput();
t->blink_state = SDL_TRUE;
t->blink_timer = SDL_GetTicks();
}
static void
_mftk_text_defocus(struct mftk_widget *w __attribute__((__unused__)))
{
SDL_StopTextInput();
}
static int
_mftk_text_key_event(struct mftk_widget *w, SDL_Event *e)
{
struct mftk_text *t = (struct mftk_text *) w;
int len;
int i;
int newlen;
int j;
t->blink_state = SDL_TRUE;
t->blink_timer = SDL_GetTicks();
switch (e->type) {
case SDL_KEYDOWN:
switch (e->key.keysym.sym) {
case SDLK_LEFT:
--t->cur;
if (t->cur < 0) {
t->cur = 0;
}
break;
case SDLK_RIGHT:
++t->cur;
len = strlen(t->val);
if (t->cur >= len) {
t->cur = len;
}
break;
case SDLK_HOME:
t->cur = 0;
break;
case SDLK_END:
t->cur = strlen(t->val);
break;
case SDLK_BACKSPACE:
if (t->cur <= 0) {
break;
}
--t->cur;
len = strlen(t->val);
for (i = t->cur; i < len; ++i) {
t->val[i] = t->val[i + 1];
}
SDL_DestroyTexture(t->texture);
t->texture = NULL;
if (t->action == NULL) {
return 0;
}
return t->action(t->user_data, t->val);
case SDLK_DELETE:
len = strlen(t->val);
if (t->cur >= len) {
break;
}
for (i = t->cur; i < len; ++i) {
t->val[i] = t->val[i + 1];
}
SDL_DestroyTexture(t->texture);
t->texture = NULL;
if (t->action == NULL) {
return 0;
}
return t->action(t->user_data, t->val);
case SDLK_RETURN:
case SDLK_KP_ENTER:
return t->submit(t->user_data);
default:
break;
}
break;
case SDL_TEXTEDITING:
/* TODO */
break;
case SDL_TEXTINPUT:
len = strlen(t->val);
newlen = strlen(e->text.text);
if (len + newlen > t->len) {
newlen = t->len - len;
}
memcpy(t->curval, t->val + t->cur, len - t->cur);
for (i = 0, j = 0;
i < newlen && e->text.text[j] != '\0';
++j) {
if (strchr(t->allowed_chars, e->text.text[j])
== NULL) {
continue;
}
t->val[t->cur + i] = e->text.text[j];
++i;
}
t->cur += i;
memcpy(t->val + t->cur, t->curval, len - t->cur + i);
t->val[len + i] = '\0';
SDL_DestroyTexture(t->texture);
t->texture = NULL;
if (t->action == NULL) {
return 0;
}
return t->action(t->user_data, t->val);
default:
break;
}
return 0;
}
static int
_mftk_text_mouse_event(struct mftk_widget *w, SDL_Event *e,
int x __attribute__((__unused__)),
int y __attribute__((__unused__)))
{
struct mftk_text *t = (struct mftk_text *) w;
if (t->editable == SDL_FALSE) {
return 0;
}
switch (e->type) {
case SDL_MOUSEBUTTONUP:
if (e->button.button == SDL_BUTTON_LEFT) {
mftk_window_focus(w->window, w);
}
break;
default:
break;
}
return 0;
}
static int
_mftk_text_render_val(struct mftk_text *t, SDL_Renderer *renderer)
{
SDL_Surface *surface;
int max_y;
const char *c;
int glyph_max_y;
surface = TTF_RenderUTF8_Blended(t->font, t->val, _mftk_text_color);
if (surface == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create surface: %s",
TTF_GetError());
return -1;
}
t->texture = SDL_CreateTextureFromSurface(renderer, surface);
if (t->texture == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create texture: %s",
SDL_GetError());
SDL_FreeSurface(surface);
return -1;
}
max_y = 0;
for (c = t->val; *c != '\0'; ++c) {
if (TTF_GlyphMetrics(t->font, *c, NULL, NULL, NULL,
&glyph_max_y, NULL) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't get glyph metrics: %s",
TTF_GetError());
continue;
}
if (glyph_max_y > max_y) {
max_y = glyph_max_y;
}
}
t->y = (t->line_skip - t->ascent) / 2 + (t->ascent - max_y);
t->w = surface->w;
t->h = surface->h;
SDL_FreeSurface(surface);
return 0;
}
static int
_mftk_text_render(struct mftk_widget *w, SDL_Renderer *renderer, int x, int y)
{
struct mftk_text *t = (struct mftk_text *) w;
SDL_Rect rect;
SDL_Color color;
int cur_x;
Uint32 timer;
if (t->val[0] != '\0' && t->texture == NULL &&
_mftk_text_render_val(t, renderer) < 0) {
return -1;
}
if (t->editable == SDL_TRUE) {
rect.x = x;
rect.y = y;
rect.w = w->w;
rect.h = w->h;
if (w->focused == SDL_TRUE) {
color.r = MFTK_COLOR_BDRF_R;
color.g = MFTK_COLOR_BDRF_G;
color.b = MFTK_COLOR_BDRF_B;
color.a = MFTK_COLOR_BDRF_A;
} else {
color.r = MFTK_COLOR_BDRN_R;
color.g = MFTK_COLOR_BDRN_G;
color.b = MFTK_COLOR_BDRN_B;
color.a = MFTK_COLOR_BDRN_A;
}
if (SDL_SetRenderDrawColor(renderer, color.r, color.g,
color.b, color.a) < 0 ||
SDL_RenderFillRect(renderer, &rect) < 0) {
goto err;
}
rect.x += t->border;
rect.y += t->border;
rect.w -= t->border * 2;
rect.h -= t->border * 2;
if (SDL_SetRenderDrawColor(renderer,
MFTK_COLOR_BACK_R, MFTK_COLOR_BACK_G,
MFTK_COLOR_BACK_B, MFTK_COLOR_BACK_A) <
0 || SDL_RenderFillRect(renderer, &rect) < 0) {
goto err;
}
}
x += t->padding;
y += t->padding;
rect.x = x;
rect.y = t->y + y;
rect.w = t->w;
rect.h = t->h;
memcpy(t->curval, t->val, t->cur);
t->curval[t->cur] = '\0';
TTF_SizeUTF8(t->font, t->curval, &cur_x, NULL);
if (t->val[0] != '\0' &&
SDL_RenderCopy(renderer, t->texture, NULL, &rect) < 0) {
goto err;
}
timer = SDL_GetTicks();
if (timer - t->blink_timer >= MFTK_CURSOR_BLINK_MS) {
if (t->blink_state == SDL_TRUE) {
t->blink_state = SDL_FALSE;
} else if (t->blink_state == SDL_FALSE) {
t->blink_state = SDL_TRUE;
}
t->blink_timer = timer;
}
if (w->focused == SDL_TRUE && t->blink_state == SDL_TRUE) {
if (SDL_SetRenderDrawColor(renderer,
_mftk_text_color.r, _mftk_text_color.g,
_mftk_text_color.b, _mftk_text_color.a)
< 0 ||
SDL_RenderDrawLine(renderer,
x + cur_x, y + t->y,
x + cur_x, y + t->h) < 0) {
goto err;
}
}
return 0;
err:
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't render widget: %s", SDL_GetError());
return -1;
}
static void
_mftk_text_destroy(struct mftk_widget *w)
{
struct mftk_text *t = (struct mftk_text *) w;
free(t->val);
free(t->curval);
SDL_DestroyTexture(t->texture);
}
struct mftk_widget *
mftk_text_new(const char *allowed_chars, int len, const char *val,
TTF_Font *font, int padding, int border, int editable,
int (*action)(void *, const char *), int (*submit)(void *),
void *user_data)
{
struct mftk_widget *w;
struct mftk_text *t;
int advance;
char ch;
if (editable == SDL_TRUE) {
mftk_widget_init_focusable(w, t, text);
} else {
mftk_widget_init(w, t, text);
}
t->allowed_chars = allowed_chars;
t->len = len;
t->font = font;
t->line_skip = TTF_FontLineSkip(font);
t->ascent = TTF_FontAscent (font);
t->texture = NULL;
t->padding = padding;
t->border = border;
t->editable = editable;
t->blink_state = SDL_TRUE;
t->blink_timer = 0;
t->action = action;
t->submit = submit;
t->user_data = user_data;
t->val = calloc(len + 1, sizeof(*t->val));
if (t->val == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create widget: %s",
strerror(errno));
free(w);
return NULL;
}
t->curval = calloc(len + 1, sizeof(*t->curval));
if (t->curval == NULL) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't create widget: %s",
strerror(errno));
free(t->curval);
free(w);
return NULL;
}
t->cur = strlen(val);
if (t->cur > t->len) {
t->cur = t->len;
}
memcpy(t->val, val, t->cur);
w->w = 0;
for (ch = allowed_chars[0]; ch != '\0'; ++ch) {
if (TTF_GlyphMetrics(font, ch, NULL, NULL, NULL, NULL, &advance)
< 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Couldn't get glyph metrics: %s",
TTF_GetError());
continue;
}
advance *= len;
if (advance > w->w) {
w->w = advance;
}
}
w->h = t->line_skip;
if (editable == SDL_TRUE) {
w->w += padding * 2 + border * 2;
w->h += padding * 2 + border * 2;
}
return w;
}
void
mftk_text_set_value(struct mftk_widget *w, const char *val)
{
struct mftk_text *t = (struct mftk_text *) w;
t->cur = strlen(val);
if (t->cur > t->len) {
t->cur = t->len;
}
memcpy(t->val, val, t->cur);
t->val[t->cur] = '\0';
SDL_DestroyTexture(t->texture);
t->texture = NULL;
}