libs/justui: add a demo of the JustUI widget library
This commit is contained in:
parent
ca2d731f39
commit
9a9c366821
|
@ -10,6 +10,7 @@ include(Fxconv)
|
|||
find_package(Gint 2.1 REQUIRED)
|
||||
find_package(LibProf 2.1 REQUIRED)
|
||||
find_package(LibImg 2.1 REQUIRED)
|
||||
find_package(JustUI 1.0 REQUIRED)
|
||||
|
||||
set(SOURCES
|
||||
src/gintctl.c
|
||||
|
@ -32,6 +33,7 @@ set(SOURCES
|
|||
src/gint/timer_callbacks.c
|
||||
src/gint/tlb.c
|
||||
src/gint/topti.c
|
||||
src/libs/justui.c
|
||||
src/libs/libimg.c
|
||||
src/libs/memory.c
|
||||
src/libs/openlibm.c
|
||||
|
@ -46,10 +48,13 @@ set(SOURCES
|
|||
src/perf/memory.s
|
||||
src/perf/render.c
|
||||
src/regs/regs.c
|
||||
src/widgets/gscreen.c
|
||||
src/widgets/gtable.c
|
||||
)
|
||||
set(ASSETS_fx
|
||||
assets-fx/fonts/hexa.png
|
||||
assets-fx/fonts/mini.png
|
||||
assets-fx/fonts/title.png
|
||||
assets-fx/fonts/uf5x7
|
||||
assets-fx/img/bopti_1col.png
|
||||
assets-fx/img/bopti_2col.png
|
||||
|
@ -69,6 +74,7 @@ set(ASSETS_fx
|
|||
assets-fx/img/opt_gint_timer_callbacks.png
|
||||
assets-fx/img/opt_gint_timers.png
|
||||
assets-fx/img/opt_gint_tlb.png
|
||||
assets-fx/img/opt_libs_jui.png
|
||||
assets-fx/img/opt_main.png
|
||||
assets-fx/img/opt_mem.png
|
||||
assets-fx/img/opt_perf_libprof.png
|
||||
|
@ -117,7 +123,7 @@ target_include_directories(gintctl PRIVATE
|
|||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${FXSDK_COMPILER_INSTALL}/include/openlibm")
|
||||
target_link_libraries(gintctl
|
||||
Gint::Gint LibProf::LibProf LibImg::LibImg -lopenlibm)
|
||||
Gint::Gint LibProf::LibProf LibImg::LibImg JustUI::JustUI -lopenlibm)
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
generate_g1a(TARGET gintctl OUTPUT "gintctl.g1a"
|
||||
|
|
|
@ -14,6 +14,13 @@ mini.png:
|
|||
grid.size: 5x6
|
||||
grid.padding: 1
|
||||
|
||||
title.png:
|
||||
type: font
|
||||
name: font_title
|
||||
charset: print
|
||||
grid.size: 5x6
|
||||
grid.padding: 1
|
||||
|
||||
uf5x7:
|
||||
type: font
|
||||
name: font_uf5x7
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -20,4 +20,7 @@ void gintctl_libs_openlibm(void);
|
|||
/* gintctl_libs_libimg(): libimg-based rendering and image transform */
|
||||
void gintctl_libs_libimg(void);
|
||||
|
||||
/* gintctl_libs_justui(): Just User Interfaces */
|
||||
void gintctl_libs_justui(void);
|
||||
|
||||
#endif /* GINTCTL_LIBS */
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
//---
|
||||
// gintctl.widgets.gscreen: gintctl's extended standard scene
|
||||
//---
|
||||
|
||||
#ifndef _GINTCTL_WIDGETS_GSCREEN
|
||||
#define _GINTCTL_WIDGETS_GSCREEN
|
||||
|
||||
#include <justui/jscene.h>
|
||||
#include <justui/jlabel.h>
|
||||
#include <justui/jfkeys.h>
|
||||
#include <gintctl/util.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
struct gscreen_tab;
|
||||
|
||||
/* gscreen: gintctl's standard screen.
|
||||
|
||||
The gscreen is a scene with a stylized title bar, an optional function key
|
||||
bar at the bottom, and a stacked layout of tabs in the middle. Each tab can
|
||||
have specific properties set, and the gscreen handles some of the focus
|
||||
management caused by hiding and showing a lot of widgets in tabs.
|
||||
|
||||
The scene of a gscreen can be accessed with (the_gscreen->scene). */
|
||||
typedef struct {
|
||||
jscene *scene;
|
||||
/* Number of tabs */
|
||||
int tab_count;
|
||||
/* Information on tabs */
|
||||
struct gscreen_tab *tabs;
|
||||
/* Fixed widets */
|
||||
jlabel *title;
|
||||
jfkeys *fkeys;
|
||||
|
||||
} gscreen;
|
||||
|
||||
struct gscreen_tab {
|
||||
/* TODO: gscreen: Hide title bar and/or status bar */
|
||||
bool title_visible, fkeys_visible;
|
||||
/* Most recent focused widget one */
|
||||
jwidget *focus;
|
||||
};
|
||||
|
||||
/* gscreen_create(): Set up a standard scene.
|
||||
If (title = NULL), the scene has no title bar, if (fkeys = NULL) it has no
|
||||
function bar. To show a title/function bar on some tabs but not all, create
|
||||
one here and use gscreen_set_tab_{title,fkeys}_visible(). */
|
||||
|
||||
#ifdef FX9860G
|
||||
gscreen *gscreen_create(char const *title, bopti_image_t const *fkeys);
|
||||
#define gscreen_create2(short, img, long, fkeys) gscreen_create(short, img)
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
gscreen *gscreen_create(char const *title, char const *fkeys);
|
||||
#define gscreen_create2(short, img, long, fkeys) gscreen_create(long, fkeys)
|
||||
#endif
|
||||
|
||||
/* gscreen_add_tab(): Add a tab to the stacked layout
|
||||
The child's parent will be changed. The widget will also have a stretch of
|
||||
(1, 1, false) set by default. If not NULL, the last parameter indicates
|
||||
which widget to focus when the tab is first shown. */
|
||||
void gscreen_add_tab(gscreen *scene, void *widget, void *focus);
|
||||
|
||||
/* gcreen_add_tabs(): Add several tabs at once, with no special parameters */
|
||||
void gscreen_add_tabs(gscreen *s, ...);
|
||||
#define gscreen_add_tabs(...) gscreen_add_tabs(__VA_ARGS__, NULL)
|
||||
|
||||
/* gscreen_show_tab(): Show a tab from the stack
|
||||
Returns true if the tab changed, false if it was already active or if it is
|
||||
an invalid tab widget. */
|
||||
bool gscreen_show_tab(gscreen *s, int tab);
|
||||
|
||||
/* gscreen_current_tab(): Give the currently visible tab */
|
||||
int gscreen_current_tab(gscreen *s);
|
||||
|
||||
/* gscreen_in(): Check if we're in a specific tab */
|
||||
bool gscreen_in(gscreen *s, int tab);
|
||||
|
||||
/* gscreen_set_tab_title_visible(): Set whether title bar is shown on a tab */
|
||||
void gscreen_set_tab_title_visible(gscreen *s, int tab, bool visible);
|
||||
|
||||
/* gscreen_set_tab_fkeys_visible(): Set whether fkeys are shown on a tab */
|
||||
void gscreen_set_tab_fkeys_visible(gscreen *s, int tab, bool visible);
|
||||
|
||||
//---
|
||||
// Focus management
|
||||
//---
|
||||
|
||||
/* Set focus for the current tab */
|
||||
void gscreen_focus(gscreen *s, void *widget);
|
||||
|
||||
#endif /* _GINTCTL_WIDGETS_GSCREEN */
|
|
@ -0,0 +1,125 @@
|
|||
//---
|
||||
// gintctl.widgets.gtable: gintctl's scrolling tables
|
||||
//---
|
||||
|
||||
#ifndef _GINTCTL_WIDGETS_GTABLE
|
||||
#define _GINTCTL_WIDGETS_GTABLE
|
||||
|
||||
#include <justui/jwidget.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
/* gtable: A dynamic scrolling table
|
||||
|
||||
This widget is a table with a header and a set of rows, that scrolls
|
||||
vertically to show more rows than there is space available on-screen.
|
||||
|
||||
The columns all have their own width, label, and font. During rendering, the
|
||||
data is provided to the table through a user-provided function that
|
||||
generates strings for every column in a chosen row. */
|
||||
typedef struct gtable {
|
||||
jwidget widget;
|
||||
/* Column details */
|
||||
struct gtable_column *meta;
|
||||
/* Function to generate the strings for a row */
|
||||
void (*generator)(struct gtable *g, int row, j_arg_t arg);
|
||||
j_arg_t arg;
|
||||
/* Number of columns, number of rows visible on-screen, number of rows,
|
||||
and current top row */
|
||||
uint8_t columns;
|
||||
uint8_t visible;
|
||||
uint16_t rows;
|
||||
uint16_t offset;
|
||||
/* Row height */
|
||||
uint8_t row_height;
|
||||
/* Additional row spacing */
|
||||
uint8_t row_spacing;
|
||||
|
||||
/* Coordinates during rendering */
|
||||
int16_t x, y;
|
||||
|
||||
} gtable;
|
||||
|
||||
/* gtable_create(): Create a scrolling table
|
||||
|
||||
The number of columns of the table must be fixed, while the number of rows
|
||||
can be set and changed later with gtable_set_rows().
|
||||
|
||||
The generator is a function that will be called during rendering to generate
|
||||
strings for each row. It should have the following prototype, where the last
|
||||
argument is an optional object of a type listed in j_arg_t (essentially an
|
||||
integer or a pointer).
|
||||
|
||||
void generator(gtable *g, int row [, <an type of j_arg_t>])
|
||||
|
||||
The generator should return all the strings for the row by a call to
|
||||
gtable_provide(). */
|
||||
gtable *gtable_create(int columns, void *generator, j_arg_t arg, void *parent);
|
||||
|
||||
/* gtable_provide(): Pass strings to display on a given row
|
||||
|
||||
The strings should be passed as variable arguments. A NULL terminator is
|
||||
added automatically. The strings will not be used after the call finishes,
|
||||
so they can be allocated statically or on the stack of the caller. */
|
||||
void gtable_provide(gtable *g, ...);
|
||||
#define gtable_provide(...) gtable_provide(__VA_ARGS__, NULL)
|
||||
|
||||
//---
|
||||
// Configuration of columns
|
||||
//---
|
||||
|
||||
/* gtable_set_column_title(): Set the title of a column */
|
||||
void gtable_set_column_title(gtable *t, int column, char const *title);
|
||||
|
||||
/* gtable_set_column_font(): Set the font of a column */
|
||||
void gtable_set_column_font(gtable *t, int column, font_t const *font);
|
||||
|
||||
/* gtable_set_column_size(): Set the size of a column
|
||||
This function sets the size of a column in relative units. During layout,
|
||||
the width of the table will be split, and each column will receive space
|
||||
proportional to their size setting. The default is 1 for all columns. */
|
||||
void gtable_set_column_size(gtable *t, int column, uint size);
|
||||
|
||||
/* The previous functions have group-setting functions to set properties for
|
||||
all columns at once. Simply list the headers/fonts/widths all at once;
|
||||
columns 0 through the number of args will be assigned. */
|
||||
|
||||
void gtable_set_column_titles(gtable *t, ...);
|
||||
#define gtable_set_column_titles(...) \
|
||||
gtable_set_column_titles(__VA_ARGS__, NULL)
|
||||
|
||||
void gtable_set_column_sizes(gtable *t, ...);
|
||||
#define gtable_set_column_sizes(...) \
|
||||
gtable_set_column_sizes(__VA_ARGS__, 0)
|
||||
|
||||
void gtable_set_column_fonts(gtable *t, ...);
|
||||
#define gtable_set_column_fonts(...) \
|
||||
gtable_set_column_fonts(__VA_ARGS__, NULL)
|
||||
|
||||
/* gtable_set_font(): Set the font for all columns */
|
||||
void gtable_set_font(gtable *t, font_t const *font);
|
||||
|
||||
//---
|
||||
// Configuration of rows
|
||||
//---
|
||||
|
||||
/* gtable_set_rows(): Set the number of rows */
|
||||
void gtable_set_rows(gtable *t, int rows);
|
||||
|
||||
/* gtable_set_row_height(): Fix row height instead of guessing from font
|
||||
Setting 0 will reset to the default behavior. */
|
||||
void gtable_set_row_height(gtable *t, int row_height);
|
||||
|
||||
/* gtable_set_row_spacing(): Set additional row spacing */
|
||||
void gtable_set_row_spacing(gtable *t, int row_spacing);
|
||||
|
||||
//---
|
||||
// Movement
|
||||
//---
|
||||
|
||||
/* gtable_scroll_to(): Scroll to the specified offset (if acceptable) */
|
||||
void gtable_scroll_to(gtable *t, int offset);
|
||||
|
||||
/* gtable_end(): Offset of the end of the table */
|
||||
int gtable_end(gtable *t);
|
||||
|
||||
#endif /* _GINTCTL_WIDGETS_GTABLE */
|
|
@ -92,6 +92,8 @@ struct menu menu_libs = {
|
|||
gintctl_libs_openlibm, 0 },
|
||||
{ "libimg" _("",": Image transforms"),
|
||||
gintctl_libs_libimg, 0 },
|
||||
{ "JustUI widgets",
|
||||
gintctl_libs_justui, 0 },
|
||||
{ NULL, NULL, 0 },
|
||||
}};
|
||||
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
#include <gint/keyboard.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/std/stdio.h>
|
||||
|
||||
#include <gintctl/libs.h>
|
||||
#include <gintctl/widgets/gscreen.h>
|
||||
#include <gintctl/widgets/gtable.h>
|
||||
#include <gintctl/util.h>
|
||||
#include <gintctl/assets.h>
|
||||
|
||||
#include <justui/jscene.h>
|
||||
#include <justui/jwidget.h>
|
||||
#include <justui/jlabel.h>
|
||||
#include <justui/jinput.h>
|
||||
#include <justui/jpainted.h>
|
||||
#include <justui/jfkeys.h>
|
||||
|
||||
static int recursive_widget_count(void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
|
||||
int total = 1;
|
||||
for(int k = 0; k < w->child_count; k++)
|
||||
total += recursive_widget_count(w->children[k]);
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static int nth_child(void *w0, int n, int l, jwidget **result, int *level)
|
||||
{
|
||||
J_CAST(w)
|
||||
if(n-- == 0)
|
||||
{
|
||||
*result = w;
|
||||
*level = l;
|
||||
return n;
|
||||
}
|
||||
|
||||
for(int k = 0; k < w->child_count && n >= 0; k++)
|
||||
n = nth_child(w->children[k], n, l+1, result, level);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void widget_tree_gen(gtable *t, int row, jwidget *root)
|
||||
{
|
||||
jwidget *w = NULL;
|
||||
int indent;
|
||||
nth_child(root, row, 0, &w, &indent);
|
||||
if(!w) return;
|
||||
|
||||
char c1[32], c2[16], c3[16];
|
||||
for(int i = 0; i < indent; i++) c1[i] = ' ';
|
||||
sprintf(c1 + indent, "%s", jwidget_type(w));
|
||||
sprintf(c2, "%dx%d", jwidget_full_width(w), jwidget_full_height(w));
|
||||
sprintf(c3, "%dx%d", jwidget_content_width(w), jwidget_content_height(w));
|
||||
|
||||
gtable_provide(t, c1, c2, c3);
|
||||
}
|
||||
|
||||
static void paint_pattern(int x, int y)
|
||||
{
|
||||
for(int dx = 0; dx < 12; dx++)
|
||||
for(int dy = 0; dy < 12; dy++)
|
||||
{
|
||||
if(((x + dx) ^ (y + dy)) & 1) dpixel(x + dx, y + dy, C_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
static void table_gen(gtable *t, int row)
|
||||
{
|
||||
char c1[16], c2[16], c3[16];
|
||||
sprintf(c1, "%d:1", row);
|
||||
sprintf(c2, "%d:2", row);
|
||||
sprintf(c3, "%d:%d", row, t->visible);
|
||||
gtable_provide(t, c1, c2, c3);
|
||||
}
|
||||
|
||||
/* gintctl_libs_justui(): Just User Interfaces */
|
||||
void gintctl_libs_justui(void)
|
||||
{
|
||||
gscreen *scr = gscreen_create2("JustUI Widgets", &img_opt_libs_jui,
|
||||
"JustUI graphical interfaces", "/SCENE;/TREE;;;;");
|
||||
|
||||
// Sample GUI
|
||||
|
||||
jwidget *tab1 = jwidget_create(NULL);
|
||||
jlayout_set_vbox(tab1)->spacing = _(1,2);
|
||||
|
||||
jwidget *c = jwidget_create(tab1);
|
||||
jlabel *c1 = jlabel_create("", c);
|
||||
jpainted_create(paint_pattern, NULL, 12, 12, c);
|
||||
gtable *c3 = gtable_create(3, table_gen, NULL, c);
|
||||
|
||||
jwidget_set_border(c, J_BORDER_SOLID, 1, C_BLACK);
|
||||
jwidget_set_padding(c, 1, 1, 1, 1);
|
||||
jwidget_set_stretch(c, 1, 1, false);
|
||||
jlayout_set_hbox(c)->spacing = _(1,2);
|
||||
|
||||
jlabel_set_font(c1, _(&font_uf5x7, dfont_default()));
|
||||
jlabel_set_alignment(c1, J_ALIGN_CENTER);
|
||||
jlabel_asprintf(c1, "Test\nlαbel\nxy=%d,%d", 7, 8);
|
||||
jwidget_set_border(c1, J_BORDER_SOLID, 1, C_BLACK);
|
||||
jwidget_set_stretch(c1, 1, 1, false);
|
||||
|
||||
jwidget_set_stretch(c3, 2, 0, false);
|
||||
gtable_set_rows(c3, 5);
|
||||
gtable_set_column_titles(c3, "C1", "C2", "Column 3");
|
||||
gtable_set_column_sizes(c3, 1, 1, 3);
|
||||
gtable_set_font(c3, &font_mini);
|
||||
|
||||
jinput *input = jinput_create("Prompt:" _(," "), 12, tab1);
|
||||
jwidget_set_stretch(input, 1, 0, false);
|
||||
jinput_set_font(input, _(&font_uf5x7, dfont_default()));
|
||||
|
||||
// Widget tree visualisation
|
||||
|
||||
gtable *tree = gtable_create(3, widget_tree_gen, scr->scene, NULL);
|
||||
gtable_set_column_titles(tree, "Type", "Size", "Content");
|
||||
gtable_set_column_sizes(tree, 3, 2, 2);
|
||||
gtable_set_row_spacing(tree, 2);
|
||||
gtable_set_font(tree, &font_mini);
|
||||
|
||||
// Scene setup
|
||||
|
||||
gscreen_add_tab(scr, tab1, c3);
|
||||
gscreen_add_tab(scr, tree, tree);
|
||||
jscene_set_focused_widget(scr->scene, c3);
|
||||
gtable_set_rows(tree, recursive_widget_count(scr->scene));
|
||||
gscreen_set_tab_title_visible(scr, 1, false);
|
||||
|
||||
jevent e;
|
||||
key_event_t k;
|
||||
int key = 0;
|
||||
|
||||
while(key != KEY_EXIT)
|
||||
{
|
||||
jscene_run(scr->scene, &e, &k);
|
||||
|
||||
if(e.type == JSCENE_PAINT)
|
||||
{
|
||||
dclear(C_WHITE);
|
||||
jscene_render(scr->scene);
|
||||
dupdate();
|
||||
}
|
||||
|
||||
if(e.type == JINPUT_VALIDATED && e.source == input)
|
||||
{
|
||||
gscreen_focus(scr, c3);
|
||||
jlabel_snprintf(c1, 20, "New!\n%s", jinput_value(input));
|
||||
}
|
||||
if(e.type == JINPUT_CANCELED && e.source == input)
|
||||
{
|
||||
gscreen_focus(scr, c3);
|
||||
}
|
||||
|
||||
if(k.type != KEYEV_DOWN) continue;
|
||||
key = k.key;
|
||||
|
||||
if(key == KEY_F3 && gscreen_in(scr, 0))
|
||||
{
|
||||
bool input_focused = (jscene_focused_widget(scr->scene) == input);
|
||||
gscreen_focus(scr, input_focused ? (void *)c3 : (void *)input);
|
||||
}
|
||||
|
||||
if(key == KEY_F1) gscreen_show_tab(scr, 0);
|
||||
if(key == KEY_F2) gscreen_show_tab(scr, 1);
|
||||
|
||||
#ifdef FX9860G
|
||||
if(key == KEY_F6) screen_mono(u"\\\\fls0\\justui.bin");
|
||||
#endif
|
||||
}
|
||||
|
||||
jwidget_destroy(scr->scene);
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
#include <gintctl/widgets/gscreen.h>
|
||||
#include <gintctl/assets.h>
|
||||
#include <gintctl/util.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
|
||||
#include <justui/jscene.h>
|
||||
#include <justui/jlabel.h>
|
||||
#include <justui/jfkeys.h>
|
||||
|
||||
#ifdef FX9860G
|
||||
gscreen *gscreen_create(char const *name, bopti_image_t const *img)
|
||||
#endif
|
||||
#ifdef FXCG50
|
||||
gscreen *gscreen_create(char const *name, char const *labels)
|
||||
#endif
|
||||
{
|
||||
gscreen *g = malloc(sizeof *g);
|
||||
if(!g) return NULL;
|
||||
|
||||
jscene *s = jscene_create_fullscreen(NULL);
|
||||
if(!s) { free(g); return NULL; }
|
||||
|
||||
g->scene = s;
|
||||
g->tabs = NULL;
|
||||
g->tab_count = 0;
|
||||
|
||||
jlabel *title = name ? jlabel_create(name, s) : NULL;
|
||||
jwidget *stack = jwidget_create(s);
|
||||
jfkeys *fkeys = _(img,labels) ? jfkeys_create(_(img,labels), s) : NULL;
|
||||
|
||||
if((name && !title) || !stack || (_(img,labels) && !fkeys)) {
|
||||
jwidget_destroy(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g->title = title;
|
||||
g->fkeys = fkeys;
|
||||
|
||||
jlayout_set_vbox(s)->spacing = _(1,3);
|
||||
jlayout_set_stack(stack);
|
||||
|
||||
if(title) {
|
||||
jwidget_set_background(title, C_BLACK);
|
||||
jlabel_set_text_color(title, C_WHITE);
|
||||
jlabel_set_font(title, _(&font_title, dfont_default()));
|
||||
jwidget_set_stretch(title, 1, 0, false);
|
||||
|
||||
#ifdef FX9860G
|
||||
jwidget_set_padding(title, 1, 1, 0, 1);
|
||||
jwidget_set_margin(title, 0, 0, 1, 0);
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
jwidget_set_padding(title, 3, 6, 3, 6);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FXCG50
|
||||
jwidget_set_padding(stack, 1, 3, 1, 3);
|
||||
#endif
|
||||
|
||||
jwidget_set_stretch(stack, 1, 1, false);
|
||||
return g;
|
||||
}
|
||||
|
||||
void gscreen_add_tab(gscreen *s, void *widget, void *focus)
|
||||
{
|
||||
struct gscreen_tab *t = realloc(s->tabs, (s->tab_count+1) * sizeof *t);
|
||||
if(!t) return;
|
||||
|
||||
s->tabs = t;
|
||||
s->tabs[s->tab_count].title_visible = (s->title != NULL);
|
||||
s->tabs[s->tab_count].fkeys_visible = (s->fkeys != NULL);
|
||||
s->tabs[s->tab_count].focus = focus;
|
||||
s->tab_count++;
|
||||
|
||||
jwidget *stack = s->scene->widget.children[1];
|
||||
jwidget_add_child(stack, widget);
|
||||
jwidget_set_stretch(widget, 1, 1, false);
|
||||
}
|
||||
|
||||
#undef gscreen_add_tabs
|
||||
void gscreen_add_tabs(gscreen *s, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
jwidget *w;
|
||||
|
||||
while((w = va_arg(args, jwidget *)))
|
||||
gscreen_add_tab(s, w, NULL);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
bool gscreen_show_tab(gscreen *s, int tab)
|
||||
{
|
||||
jwidget *stack = s->scene->widget.children[1];
|
||||
jlayout_stack *l = jlayout_get_stack(stack);
|
||||
|
||||
/* Find widget ID in the stack
|
||||
int i = 0;
|
||||
while(i < stack->child_count && stack->children[i] != widget) i++;
|
||||
if(i >= stack->child_count || l->active == i) return false; */
|
||||
if(tab < 0 || tab >= stack->child_count) return false;
|
||||
|
||||
/* Update keyboard focus */
|
||||
s->tabs[l->active].focus = jscene_focused_widget(s->scene);
|
||||
jscene_set_focused_widget(s->scene, s->tabs[tab].focus);
|
||||
|
||||
l->active = tab;
|
||||
stack->update = 1;
|
||||
|
||||
/* Hide or show title and function key bar as needed */
|
||||
jwidget_set_visible(s->title, s->tabs[tab].title_visible);
|
||||
if(s->fkeys) jwidget_set_visible(s->fkeys, s->tabs[tab].fkeys_visible);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int gscreen_current_tab(gscreen *s)
|
||||
{
|
||||
jwidget *stack = s->scene->widget.children[1];
|
||||
jlayout_stack *l = jlayout_get_stack(stack);
|
||||
return l->active;
|
||||
}
|
||||
|
||||
bool gscreen_in(gscreen *s, int tab)
|
||||
{
|
||||
return gscreen_current_tab(s) == tab;
|
||||
}
|
||||
|
||||
void gscreen_set_tab_title_visible(gscreen *s, int tab, bool visible)
|
||||
{
|
||||
if(tab < 0 || tab >= s->tab_count) return;
|
||||
s->tabs[tab].title_visible = visible;
|
||||
}
|
||||
|
||||
void gscreen_set_tab_fkeys_visible(gscreen *s, int tab, bool visible)
|
||||
{
|
||||
if(tab < 0 || tab >= s->tab_count) return;
|
||||
s->tabs[tab].fkeys_visible = visible;
|
||||
}
|
||||
|
||||
void gscreen_focus(gscreen *s, void *widget)
|
||||
{
|
||||
return jscene_set_focused_widget(s->scene, widget);
|
||||
}
|
|
@ -0,0 +1,354 @@
|
|||
#include <gintctl/widgets/gtable.h>
|
||||
#include <justui/jwidget-api.h>
|
||||
#include <gint/std/stdlib.h>
|
||||
#include <gintctl/util.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct gtable_column {
|
||||
char const *title;
|
||||
font_t const *font;
|
||||
uint16_t size;
|
||||
uint16_t width;
|
||||
};
|
||||
|
||||
/* Type identifier for gtable */
|
||||
static int gtable_type_id = -1;
|
||||
|
||||
void update_visible(gtable *t);
|
||||
|
||||
gtable *gtable_create(int columns, void *generator, j_arg_t arg, void *parent)
|
||||
{
|
||||
if(gtable_type_id < 0) return NULL;
|
||||
|
||||
gtable *t = malloc(sizeof *t);
|
||||
if(!t) return NULL;
|
||||
|
||||
t->meta = malloc(columns * sizeof *t->meta);
|
||||
if(!t->meta) { free(t); return NULL; }
|
||||
|
||||
jwidget_init(&t->widget, gtable_type_id, parent);
|
||||
|
||||
t->generator = generator;
|
||||
t->arg = arg;
|
||||
|
||||
t->columns = columns;
|
||||
t->rows = 0;
|
||||
t->offset = 0;
|
||||
t->visible = 0;
|
||||
|
||||
t->row_height = 0;
|
||||
t->row_spacing = 1;
|
||||
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
t->meta[i].title = NULL;
|
||||
t->meta[i].font = dfont_default();
|
||||
t->meta[i].width = 1;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
//---
|
||||
// Configuration of columns
|
||||
//---
|
||||
|
||||
void gtable_set_column_title(gtable *t, int column, char const *title)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].title = title;
|
||||
t->widget.update = 1;
|
||||
}
|
||||
|
||||
void gtable_set_column_font(gtable *t, int column, font_t const *font)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].font = (font ? font : dfont_default());
|
||||
t->widget.update = 1;
|
||||
}
|
||||
|
||||
void gtable_set_column_size(gtable *t, int column, uint size)
|
||||
{
|
||||
if(column < 0 || column >= t->columns) return;
|
||||
t->meta[column].size = size;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
#undef gtable_set_column_titles
|
||||
void gtable_set_column_titles(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
char const *title = va_arg(args, char const *);
|
||||
if(!title) break;
|
||||
gtable_set_column_title(t, i, title);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#undef gtable_set_column_fonts
|
||||
void gtable_set_column_fonts(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
font_t const *font = va_arg(args, font_t const *);
|
||||
if(!font) break;
|
||||
gtable_set_column_font(t, i, font);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#undef gtable_set_column_sizes
|
||||
void gtable_set_column_sizes(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++)
|
||||
{
|
||||
uint size = va_arg(args, uint);
|
||||
if(!size) break;
|
||||
gtable_set_column_size(t, i, size);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void gtable_set_font(gtable *t, font_t const *font)
|
||||
{
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
gtable_set_column_font(t, i, font);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Configuration of rows
|
||||
//---
|
||||
|
||||
void gtable_set_rows(gtable *t, int rows)
|
||||
{
|
||||
/* The re-layout will make sure we stay in the visible range */
|
||||
t->rows = max(rows, 0);
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
void gtable_set_row_height(gtable *t, int row_height)
|
||||
{
|
||||
t->row_height = row_height;
|
||||
t->visible = 0;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
void gtable_set_row_spacing(gtable *t, int row_spacing)
|
||||
{
|
||||
t->row_spacing = row_spacing;
|
||||
t->visible = 0;
|
||||
t->widget.dirty = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Movement
|
||||
//---
|
||||
|
||||
/* Guarantee that the current positioning is valid */
|
||||
static void bound(gtable *t)
|
||||
{
|
||||
t->offset = min(t->offset, gtable_end(t));
|
||||
}
|
||||
|
||||
void gtable_scroll_to(gtable *t, int offset)
|
||||
{
|
||||
t->offset = offset;
|
||||
bound(t);
|
||||
}
|
||||
|
||||
int gtable_end(gtable *t)
|
||||
{
|
||||
update_visible(t);
|
||||
return max((int)t->rows - (int)t->visible, 0);
|
||||
}
|
||||
|
||||
//---
|
||||
// Polymorphic widget operations
|
||||
//---
|
||||
|
||||
int compute_row_height(gtable *t)
|
||||
{
|
||||
int row_height = t->row_height;
|
||||
if(row_height == 0) {
|
||||
for(int i = 0; i < t->columns; i++) {
|
||||
row_height = max(row_height, t->meta[i].font->line_height);
|
||||
}
|
||||
}
|
||||
return row_height;
|
||||
}
|
||||
|
||||
/* Recompute (visible) based on the current size */
|
||||
void update_visible(gtable *t)
|
||||
{
|
||||
if(t->visible == 0) {
|
||||
int row_height = compute_row_height(t);
|
||||
int header_height = row_height + t->row_spacing + 1;
|
||||
|
||||
int space = jwidget_content_height(t) - header_height;
|
||||
t->visible = max(0, space / (row_height + t->row_spacing));
|
||||
}
|
||||
}
|
||||
|
||||
static void gtable_poly_csize(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
t->widget.w = 64;
|
||||
t->widget.h = 32;
|
||||
}
|
||||
|
||||
static void gtable_poly_layout(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
t->visible = 0;
|
||||
update_visible(t);
|
||||
bound(t);
|
||||
|
||||
int cw = jwidget_content_width(t);
|
||||
/* Leave space for a scrollbar */
|
||||
if(t->visible < t->rows) cw -= _(2,4);
|
||||
|
||||
/* Compute the width of each column */
|
||||
uint total_size = 0;
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
total_size += t->meta[i].size;
|
||||
}
|
||||
|
||||
uint space_left = cw;
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
t->meta[i].width = (t->meta[i].size * cw) / total_size;
|
||||
space_left -= t->meta[i].width;
|
||||
}
|
||||
|
||||
/* Grant space left to the first columns */
|
||||
for(uint i = 0; i < t->columns && i < space_left; i++) {
|
||||
t->meta[i].width++;
|
||||
}
|
||||
}
|
||||
|
||||
static void gtable_poly_render(void *t0, int base_x, int base_y)
|
||||
{
|
||||
gtable *t = t0;
|
||||
int row_height = compute_row_height(t);
|
||||
int cw = jwidget_content_width(t);
|
||||
int y = base_y;
|
||||
|
||||
for(uint i=0, x=base_x; i < t->columns; i++) {
|
||||
font_t const *old_font = dfont(t->meta[i].font);
|
||||
dtext(x, y, C_BLACK, t->meta[i].title);
|
||||
dfont(old_font);
|
||||
|
||||
x += t->meta[i].width;
|
||||
}
|
||||
|
||||
y += row_height + t->row_spacing;
|
||||
dline(base_x, y, base_x + cw - 1, y, C_BLACK);
|
||||
y += t->row_spacing + 1;
|
||||
int rows_y = y;
|
||||
|
||||
for(uint i = 0; t->offset + i < t->rows && i < t->visible; i++) {
|
||||
t->x = base_x;
|
||||
t->y = y;
|
||||
|
||||
t->generator(t, t->offset + i, t->arg);
|
||||
y += row_height + t->row_spacing;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
if(t->visible < t->rows) {
|
||||
/* Area where the scroll bar lives */
|
||||
int area_w = _(1,2);
|
||||
int area_x = base_x + cw - area_w;
|
||||
int area_y = rows_y;
|
||||
int area_h = jwidget_content_height(t) - (area_y - base_y);
|
||||
|
||||
/* Position and size of scroll bar within the area */
|
||||
int bar_y = (t->offset * area_h + t->rows/2) / t->rows;
|
||||
int bar_h = (t->visible * area_h + t->rows/2) / t->rows;
|
||||
|
||||
drect(area_x, area_y + bar_y, area_x + area_w - 1,
|
||||
area_y + bar_y + bar_h, C_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
#undef gtable_provide
|
||||
void gtable_provide(gtable *t, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, t);
|
||||
|
||||
for(uint i = 0; i < t->columns; i++) {
|
||||
char const *str = va_arg(args, char const *);
|
||||
if(!str) break;
|
||||
|
||||
font_t const *old_font = dfont(t->meta[i].font);
|
||||
dtext(t->x, t->y, C_BLACK, str);
|
||||
dfont(old_font);
|
||||
|
||||
t->x += t->meta[i].width;
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static bool gtable_poly_event(void *t0, jevent e)
|
||||
{
|
||||
gtable *t = t0;
|
||||
int end = gtable_end(t);
|
||||
update_visible(t);
|
||||
|
||||
if(e.type != JWIDGET_KEY) return false;
|
||||
|
||||
key_event_t kev = e.data.key;
|
||||
if(kev.type == KEYEV_UP) return false;
|
||||
|
||||
if(kev.key == KEY_DOWN && t->offset < end) {
|
||||
if(kev.shift) t->offset = end;
|
||||
else t->offset++;
|
||||
t->widget.update = 1;
|
||||
return true;
|
||||
}
|
||||
if(kev.key == KEY_UP && t->offset > 0) {
|
||||
if(kev.shift) t->offset = 0;
|
||||
else t->offset--;
|
||||
t->widget.update = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gtable_poly_destroy(void *t0)
|
||||
{
|
||||
gtable *t = t0;
|
||||
free(t->meta);
|
||||
}
|
||||
|
||||
/* gtable type definition */
|
||||
static jwidget_poly type_gtable = {
|
||||
.name = "gtable",
|
||||
.csize = gtable_poly_csize,
|
||||
.layout = gtable_poly_layout,
|
||||
.render = gtable_poly_render,
|
||||
.event = gtable_poly_event,
|
||||
.destroy = gtable_poly_destroy,
|
||||
};
|
||||
|
||||
/* Type registration */
|
||||
__attribute__((constructor(2000)))
|
||||
static void j_register_gtable(void)
|
||||
{
|
||||
gtable_type_id = j_register_widget(&type_gtable, "jwidget");
|
||||
}
|
Loading…
Reference in New Issue