220 lines
5.5 KiB
C
220 lines
5.5 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;
|
|
|
|
//=== Modifier states ===//
|
|
|
|
enum {
|
|
MOD_IDLE, /* Not active */
|
|
MOD_INSTANT, /* Instant-loaded but not yet used */
|
|
MOD_INSTANT_USED, /* Instant-loaded and has been used */
|
|
|
|
MOD_LOCKED, /* Locked */
|
|
MOD_LOCKED_INSTANT, /* Locked and instant-loaded but not used */
|
|
MOD_LOCKED_INSTANT_USED, /* Locked and instant-loaded and used */
|
|
};
|
|
|
|
/* 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;
|
|
}
|
|
|
|
//=== 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;
|
|
}
|
|
|
|
//---
|
|
// 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 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) {
|
|
s->shift = ev.type == KEYEV_UP ? mod_up(s->shift) : mod_down(s->shift);
|
|
return true;
|
|
}
|
|
if(ev.key == KEY_ALPHA) {
|
|
s->alpha = ev.type == KEYEV_UP ? mod_up(s->alpha) : mod_down(s->alpha);
|
|
return true;
|
|
}
|
|
|
|
if(ev.type == KEYEV_UP)
|
|
return false;
|
|
|
|
ev.mod = true;
|
|
ev.shift = mod_active(s->shift);
|
|
ev.alpha = mod_active(s->alpha);
|
|
|
|
s->shift = mod_down_other(s->shift);
|
|
s->alpha = mod_down_other(s->alpha);
|
|
|
|
/* TODO: Handle input events better in the shell widget! */
|
|
int c = console_key_event_to_char(ev);
|
|
/* TODO: Can widget_shell_poly_event please not call into MicroPython? */
|
|
if(c != 0) {
|
|
pyexec_event_repl_process_char(c);
|
|
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");
|
|
}
|