jbutton: add new bare-bones button widget for touch

This commit is contained in:
Lephenixnoir 2025-04-15 05:03:53 +02:00
parent bd89221d33
commit b75c8979ba
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
3 changed files with 199 additions and 0 deletions

View file

@ -34,6 +34,7 @@ add_library(${NAME} STATIC
src/jframe.c
src/jlist.c
src/jscrolledlist.c
src/jbutton.c
src/vec.c
src/keymap.c
${ASSETS_${FXSDK_PLATFORM}}

59
include/justui/jbutton.h Normal file
View file

@ -0,0 +1,59 @@
//---
// JustUI.jbutton: Touchable multi-state click button
//---
#ifndef _J_JBUTTON
#define _J_JBUTTON
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <gint/display.h>
/* jbutton: Button with multiple states
This widget is your everyday interactible button. It responds to touch by
changing its background color for idle, focused and active states. It also
has a disabled states which can be used to make a "selected" visual.
TODO: Allow jbutton it to hold any widget inside.
TODO: jbutton's focus state is untested since I only tested touch.
Events:
* JBUTTON_TRIGGERED when activated. */
enum {
JBUTTON_IDLE,
JBUTTON_ACTIVE,
JBUTTON_DISABLED,
JBUTTON_STATE_NUM,
};
typedef struct {
jwidget widget;
/* Text shown on the button; not owned by the button */
char const *text;
/* Rendering font */
font_t const *font;
/* Colors for all states */
color_t fg_colors[JBUTTON_STATE_NUM];
color_t bg_colors[JBUTTON_STATE_NUM];
/* Current state */
int state;
} jbutton;
/* Event IDs */
extern uint16_t JBUTTON_TRIGGERED;
/* jbutton_create(): Create a button */
jbutton *jbutton_create(char const *text, void *parent);
/* Trivial properties */
void jbutton_set_text(jbutton *b, char const *text);
void jbutton_set_font(jbutton *b, font_t const *font);
void jbutton_set_disabled(jbutton *b, bool disabled);
#endif /* _J_JBUTTON */

139
src/jbutton.c Normal file
View file

@ -0,0 +1,139 @@
#include <justui/jwidget.h>
#include <justui/jwidget-api.h>
#include <justui/jbutton.h>
#include <justui/config.h>
#include <stdlib.h>
J_DEFINE_WIDGET(jbutton, csize, render, event)
J_DEFINE_EVENTS(JBUTTON_TRIGGERED)
static void jbutton_set_state(jbutton *b, int state)
{
if((uint)state >= JBUTTON_STATE_NUM || b->state == state)
return;
b->state = state;
jwidget_set_background(b, b->bg_colors[state]);
b->widget.update = 1;
}
jbutton *jbutton_create(char const *text, void *parent)
{
if(jbutton_type_id < 0)
return NULL;
jbutton *b = malloc(sizeof *b);
if(!b)
return NULL;
jwidget_init(&b->widget, jbutton_type_id, parent);
jwidget_set_focus_policy(b, J_FOCUS_POLICY_ACCEPT);
b->text = text ? text : "";
b->font = dfont_default();
#if GINT_RENDER_MONO
b->fg_colors[JBUTTON_IDLE] = C_WHITE;
b->fg_colors[JBUTTON_ACTIVE] = C_WHITE;
b->fg_colors[JBUTTON_DISABLED] = C_BLACK;
b->bg_colors[JBUTTON_IDLE] = C_BLACK;
b->bg_colors[JBUTTON_ACTIVE] = C_BLACK;
b->bg_colors[JBUTTON_DISABLED] = C_WHITE;
jwidget_set_padding(b, 1, 2, 1, 2);
#endif
#if GINT_RENDER_RGB
b->fg_colors[JBUTTON_IDLE] = C_WHITE;
b->fg_colors[JBUTTON_ACTIVE] = C_WHITE;
b->fg_colors[JBUTTON_DISABLED] = C_RGB(8, 8, 8);
b->bg_colors[JBUTTON_IDLE] = C_BLACK;
b->bg_colors[JBUTTON_ACTIVE] = C_RGB(8, 8, 8);
b->bg_colors[JBUTTON_DISABLED] = C_RGB(24, 24, 24);
jwidget_set_padding(b, 2, 4, 2, 4);
#endif
jbutton_set_state(b, JBUTTON_IDLE);
return b;
}
void jbutton_set_text(jbutton *b, char const *text)
{
b->text = text ? text : "";
b->widget.dirty = 1;
}
void jbutton_set_font(jbutton *b, font_t const *font)
{
b->font = font ? font : dfont_default();
b->widget.dirty = 1;
}
void jbutton_set_disabled(jbutton *b, bool disabled)
{
if(disabled)
jbutton_set_state(b, JBUTTON_DISABLED);
else if(b->state == JBUTTON_DISABLED)
jbutton_set_state(b, JBUTTON_IDLE);
}
//---
// Polymorphic widget operations
//---
void jbutton_poly_csize(void *b0)
{
jbutton *b = b0;
int w, h;
dsize(b->text, b->font ? b->font : dfont_default(), &w, &h);
b->widget.w = w;
b->widget.h = h;
}
void jbutton_poly_render(void *b0, int x, int y)
{
jbutton *b = b0;
int cw = jwidget_content_width(b);
int ch = jwidget_content_height(b);
int fg = b->fg_colors[b->state];
font_t const *old_font = dfont(b->font);
dtext_opt(x + cw / 2, y + ch / 2, fg, C_NONE, DTEXT_CENTER, DTEXT_MIDDLE,
b->text, -1);
dfont(old_font);
}
bool jbutton_poly_event(void *b0, jevent e)
{
jbutton *b = b0;
key_event_t ev = { .type = KEYEV_NONE };
if(e.type == JWIDGET_KEY)
ev = e.key;
#if J_CONFIG_TOUCH
bool accepts_touch = b->state != JBUTTON_DISABLED;
if((ev.type == KEYEV_TOUCH_DOWN || ev.type == KEYEV_TOUCH_DRAG ||
ev.type == KEYEV_TOUCH_UP) && accepts_touch) {
int lx = ev.x - jwidget_absolute_padding_x(b);
int ly = ev.y - jwidget_absolute_padding_y(b);
uint cw = jwidget_padding_width(b);
uint ch = jwidget_padding_height(b);
bool inside = (uint)lx < cw && (uint)ly < ch;
if(ev.type == KEYEV_TOUCH_DOWN)
jbutton_set_state(b, JBUTTON_ACTIVE);
if(ev.type == KEYEV_TOUCH_DRAG)
jbutton_set_state(b, inside ? JBUTTON_ACTIVE : JBUTTON_IDLE);
if(ev.type == KEYEV_TOUCH_UP && b->state == JBUTTON_ACTIVE) {
jbutton_set_state(b, JBUTTON_IDLE);
jwidget_emit(b, (jevent){ .type = JBUTTON_TRIGGERED });
}
return true;
}
#endif
if(ev.type == KEYEV_DOWN && (ev.key == KEY_EXE || ev.key == KEY_OK)) {
jwidget_emit(b, (jevent){ .type = JBUTTON_TRIGGERED });
return true;
}
return jwidget_poly_event(b, e);
}