PythonExtra/ports/sh/widget_shell.c

253 lines
6.2 KiB
C

//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "widget_shell.h"
#include <justui/jwidget-api.h>
#include <gint/timer.h>
#include <stdlib.h>
/* Type identified for widget_shell */
static int widget_shell_id = -1;
/* Events */
uint16_t WIDGET_SHELL_MOD_CHANGED;
uint16_t WIDGET_SHELL_CHAR_INPUT;
//=== Modifier states ===//
/* Handle a key press/release for a modifier */
static int mod_down(int state)
{
if(state == MOD_IDLE)
return MOD_INSTANT;
if(state == MOD_LOCKED)
return MOD_LOCKED_INSTANT;
return state;
}
static int mod_up(int state)
{
if(state == MOD_INSTANT)
return MOD_LOCKED;
if(state == MOD_INSTANT_USED)
return MOD_IDLE;
if(state == MOD_LOCKED_INSTANT)
return MOD_IDLE;
if(state == MOD_LOCKED_INSTANT_USED)
return MOD_LOCKED;
return state;
}
/* Handle a press for another key */
static int mod_down_other(int state)
{
if(state == MOD_INSTANT)
return MOD_INSTANT_USED;
if(state == MOD_LOCKED_INSTANT)
return MOD_LOCKED_INSTANT_USED;
return state;
}
/* Whether a modifier is active */
static bool mod_active(int state)
{
return state == MOD_LOCKED || state == MOD_INSTANT
|| state == MOD_INSTANT_USED;
}
/* Whether a modifier is in an instant mode */
static bool mod_instant(int state)
{
return state != MOD_IDLE && state != MOD_LOCKED;
}
//=== Shell widget ===//
static int widget_shell_timer_handler(void *s0)
{
widget_shell *s = s0;
if(s->console && s->console->render_needed)
s->widget.update = true;
return TIMER_CONTINUE;
}
widget_shell *widget_shell_create(console_t *console, void *parent)
{
if(widget_shell_id < 0)
return NULL;
widget_shell *s = malloc(sizeof *s);
if(!s)
return NULL;
s->timer_id = timer_configure(TIMER_ANY, 1000000 / WIDGET_SHELL_FPS,
GINT_CALL(widget_shell_timer_handler, (void *)s));
if(s->timer_id < 0) {
free(s);
return NULL;
}
jwidget_init(&s->widget, widget_shell_id, parent);
s->console = console;
s->font = dfont_default();
s->color = C_BLACK;
s->line_spacing = 0;
s->lines = 0;
s->shift = MOD_IDLE;
s->alpha = MOD_IDLE;
timer_start(s->timer_id);
return s;
}
void widget_shell_set_text_color(widget_shell *s, int color)
{
s->color = color;
s->widget.update = 1;
}
void widget_shell_set_font(widget_shell *s, font_t const *font)
{
s->font = font ? font : dfont_default();
s->widget.dirty = 1;
}
void widget_shell_set_line_spacing(widget_shell *s, int line_spacing)
{
s->line_spacing = line_spacing;
s->widget.dirty = 1;
}
void widget_shell_get_modifiers(widget_shell *s, int *shift, int *alpha)
{
if(shift)
*shift = s->shift;
if(alpha)
*alpha = s->alpha;
}
void widget_shell_modifier_info(int shift, int alpha,
int *layer, bool *instant)
{
if(layer)
*layer = mod_active(shift) + 2 * mod_active(alpha);
if(instant)
*instant = mod_instant(shift) || mod_instant(alpha);
}
//---
// Polymorphic widget operations
//---
static void widget_shell_poly_csize(void *s0)
{
widget_shell *s = s0;
int row_height = s->font->line_height;
int base_rows = 4;
s->widget.w = DWIDTH / 2;
s->widget.h = row_height * base_rows + s->line_spacing * (base_rows - 1);
}
static void widget_shell_poly_layout(void *s0)
{
widget_shell *s = s0;
int ch = jwidget_content_height(s);
int line_height = s->font->line_height + s->line_spacing;
s->lines = ch / line_height;
}
static void widget_shell_poly_render(void *s0, int x, int y)
{
widget_shell *s = s0;
int line_height = s->font->line_height + s->line_spacing;
font_t const *old_font = dfont(s->font);
console_render(x, y, s->console, jwidget_content_width(s), line_height,
s->lines);
console_clear_render_flag(s->console);
dfont(old_font);
}
static void widget_shell_update_mod(widget_shell *s, key_event_t ev)
{
int *mod = (ev.key == KEY_SHIFT) ? &s->shift : &s->alpha;
int new_mod = (ev.type == KEYEV_UP) ? mod_up(*mod) : mod_down(*mod);
if(new_mod != *mod)
jwidget_emit(s, (jevent){ .type = WIDGET_SHELL_MOD_CHANGED });
*mod = new_mod;
}
static void widget_shell_use_mods(widget_shell *s)
{
int new_shift = mod_down_other(s->shift);
int new_alpha = mod_down_other(s->alpha);
if(new_shift != s->shift || new_alpha != s->alpha)
jwidget_emit(s, (jevent){ .type = WIDGET_SHELL_MOD_CHANGED });
s->shift = new_shift;
s->alpha = new_alpha;
}
static bool widget_shell_poly_event(void *s0, jevent e)
{
widget_shell *s = s0;
if(e.type != JWIDGET_KEY)
return false;
key_event_t ev = e.key;
if(ev.key == KEY_SHIFT || ev.key == KEY_ALPHA) {
widget_shell_update_mod(s, ev);
return true;
}
if(ev.type == KEYEV_UP)
return false;
ev.mod = true;
ev.shift = mod_active(s->shift);
ev.alpha = mod_active(s->alpha);
widget_shell_use_mods(s);
/* TODO: Handle input events better in the shell widget! */
int c = console_key_event_to_char(ev);
if(c != 0) {
jevent e = { .type = WIDGET_SHELL_CHAR_INPUT, .data = c };
jwidget_emit(s, e);
return true;
}
return false;
}
static void widget_shell_poly_destroy(void *s0)
{
widget_shell *s = s0;
timer_stop(s->timer_id);
}
/* widget_shell type definition */
static jwidget_poly type_widget_shell = {
.name = "widget_shell",
.csize = widget_shell_poly_csize,
.layout = widget_shell_poly_layout,
.render = widget_shell_poly_render,
.event = widget_shell_poly_event,
.destroy = widget_shell_poly_destroy,
};
__attribute__((constructor))
static void j_register_widget_shell(void)
{
widget_shell_id = j_register_widget(&type_widget_shell, "jwidget");
WIDGET_SHELL_MOD_CHANGED = j_register_event();
WIDGET_SHELL_CHAR_INPUT = j_register_event();
}