mirror of
https://git.planet-casio.com/Lephenixnoir/JustUI.git
synced 2024-12-28 20:43:40 +01:00
Compare commits
16 commits
aa736bd12a
...
7a5101360a
Author | SHA1 | Date | |
---|---|---|---|
|
7a5101360a | ||
|
f28d7a9cb8 | ||
|
93eb0df38a | ||
|
626da6f378 | ||
|
683e89d725 | ||
|
216918123f | ||
|
a2129f1ed2 | ||
|
7b8070f02c | ||
|
f32dcc69ce | ||
|
e12a58c1f0 | ||
|
7f2131d6a0 | ||
|
7587dfa17c | ||
|
12b29f8223 | ||
|
ba7b0a02d0 | ||
|
3488c6515a | ||
|
0c8371edce |
20 changed files with 472 additions and 147 deletions
105
doc/scene.md
105
doc/scene.md
|
@ -1,3 +1,106 @@
|
||||||
# JustUI: Scenes and events
|
# JustUI: Scenes, events and keyboard focus
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
The _scene_ in JustUI is the component sitting at the root of the widget hierarchy. It handles all the dynamic aspects of the UI, including distributing/propagating events and managing keyboard focus among the widgets.
|
||||||
|
|
||||||
|
Ideas...
|
||||||
|
|
||||||
|
New keyboard focus system.
|
||||||
|
1. Principle and conforming states
|
||||||
|
a. Widgets have a focus policy of FOCUS_ACCEPT, FOCUS_SCOPE or FOCUS_REJECT,
|
||||||
|
and two status flags FOCUSED and ACTIVE_FOCUSED whose valid combinations
|
||||||
|
describe 3 focus levels: *no focus* (F=0), *inactive focus* (F=1, AF=0),
|
||||||
|
and *active focus* (F=1, AF=1).
|
||||||
|
b. Each focus scope defines a region consisting of its subtree but excluding
|
||||||
|
itself and any children of other focus scopes. Up to one widget of policy
|
||||||
|
FOCUS_ACCEPT or FOCUS_SCOPE in the region may have its FOCUSED flag set,
|
||||||
|
called the *focus target* of the scope. The ACTIVE_FOCUSED flag of the
|
||||||
|
target is equal to the scope's.
|
||||||
|
c. Every focus scope fs induces a *focus chain* fc(fs), where
|
||||||
|
fc(w) = [] if w is FOCUS_ACCEPT or w is FOCUS_SCOPE with no target;
|
||||||
|
fc(w) = fc(target) + [target] if w is a scope with the named target.
|
||||||
|
Note that as a result of 1.b all elements of the chain have their FOCUSED
|
||||||
|
flag set and their ACTIVE_FOCUSED flags equal to that of fs.
|
||||||
|
d. jscene is a focus scope defined to have its ACTIVE_FOCUSED always set,
|
||||||
|
the only such widget that doesn't inherit this flag from a scope parent.
|
||||||
|
As such, the ACTIVE_FOCUSED flag identifies the focus chain of the scene.
|
||||||
|
jscene offers keyboard events to its focus chain, propagating rejected
|
||||||
|
events in list order.
|
||||||
|
X. The first (deepest) widget in the scene's focus chain is said to have
|
||||||
|
*strong focus*. Other widgets this chain are said to have *weak focus*.
|
||||||
|
-> TODO: Strong/weak focus is an informal substate of active focus and
|
||||||
|
may not be kept in the future.
|
||||||
|
2. Operations
|
||||||
|
a. The core operation is a changing the target of a scope, which can be
|
||||||
|
initiated by any widget in the region through a widget-context call. The
|
||||||
|
consequences of this operation are as follow:
|
||||||
|
- The old target, if any, loses its FOCUSED and ACTIVE_FOCUSED flags. If
|
||||||
|
it's a scope and it had active focus, all widgets in its focus chain
|
||||||
|
also lose active focus.
|
||||||
|
- The new target, if any, gets its FOCUSED flag and the scope's value for
|
||||||
|
the ACTIVE_FOCUSED flag. If it's a focus scope and it gets active
|
||||||
|
focus, then its focus chain also gets active focus.
|
||||||
|
All widgets affected receive a FOCUS_CHANGED event and the scope itself
|
||||||
|
receives a FOCUS_TARGET_CHANGED event.
|
||||||
|
b. There is a widget-scene call to give a widget active focus. If the widget
|
||||||
|
has policy FOCUS_ACCEPT or FOCUS_SCOPE, this call walks up the scope
|
||||||
|
chain and assigns new targets until it reaches the scene. If the widget
|
||||||
|
has policy FOCUS_NONE, this call just removes the surrounding scope's
|
||||||
|
target.
|
||||||
|
X. There is a widget-context call to relinquish one's own focus within the
|
||||||
|
parent scope. This is intended to differ from 2.b in that scopes might
|
||||||
|
implement special logic for moving focus to a nearby widget.
|
||||||
|
-> TODO: Left unresolved for later
|
||||||
|
3. Key-listener pattern
|
||||||
|
a. The operations described in 2.a and 2.b allow a "key-listener" pattern
|
||||||
|
where a widget "klw" (not the scene) receives keyboard events from its
|
||||||
|
potentially-focused descendants (i.e. inserts itself in the focus chain)
|
||||||
|
without impacting the focus mechanics of its surrounding scope "ssw".
|
||||||
|
b. The pattern consists of making klw a focus scope, and whenever it
|
||||||
|
receives a FOCUS_TARGET_CHANGED event that assigns a target, make klw
|
||||||
|
grab focus within ssw. This way, with regards to ssw, when focus moves:
|
||||||
|
- From within klw to within klw:
|
||||||
|
the grab is a no-op: OK.
|
||||||
|
- From within klw to outside it:
|
||||||
|
outside widget gets focus within ssw and klw does nothing: OK.
|
||||||
|
- From outside klw to within it:
|
||||||
|
inside widget gets focus in klw, klw gets focus in ssw: OK.
|
||||||
|
- From outside klw to outside it:
|
||||||
|
normal retargeting within ssw.
|
||||||
|
X. Should this be a built-in behavior?
|
||||||
|
|
||||||
|
4. Behavior of built-in widgets
|
||||||
|
a. `jfileselect`, `jinput`, `jlist` have policy FOCUS_ACCEPT
|
||||||
|
b. `jscene` has policy FOCUS_SCOPE
|
||||||
|
TODO: What about jframe?
|
||||||
|
c. `jfkeys` has policy `FOCUS_REJECT` and must be given events manually
|
||||||
|
d. Widgets with a stack layout will try and give focus (within their
|
||||||
|
surrounding scope) to the current element. If the element is complex, it
|
||||||
|
should be a focus scope. Similarly, widgets with invisible children will
|
||||||
|
move focus around to their direct children as needed to make sure focus
|
||||||
|
remains on a visible child.
|
||||||
|
|
||||||
|
5. Implementation
|
||||||
|
a. Events are `FOCUS_CHANGED` when own FOCUSED and ACTIVE_FOCUSED flags have
|
||||||
|
changed, and `FOCUS_TARGET_CHANGED` for scopes when the target changes.
|
||||||
|
|
||||||
|
NOTES:
|
||||||
|
- Focus events don't propagate but are replicated, which adds some complexity
|
||||||
|
- Focus scopes could have background focus for jfkeys (left for later). Hard
|
||||||
|
part is this isn't specified by jfkeys.
|
||||||
|
Could tell users to add this to their main loop, or follow gscreen:
|
||||||
|
jevent e = jscene_run();
|
||||||
|
if(jwidget_event(fkeys, e)) continue;
|
||||||
|
- Is there any sensible definition to "relinquishing" focus? In gintctl if e.g.
|
||||||
|
an jinput validates we want to take its focus away and put it back somewhere
|
||||||
|
sensible (i.e. the gscreen, not NULL, otherwise we don't have F-keys anymore)
|
||||||
|
- Some functions from <jwidget-api.h> should go to <jwidget.h>
|
||||||
|
- Functions from <jwidget.h> that require context or operate on the whole tree
|
||||||
|
should **really** be identified clearly. Everything by default should just
|
||||||
|
treat the widget as a box in a void.
|
||||||
|
-> Previously this was in jscene. Why not keep it that way?
|
||||||
|
|
||||||
|
## Keyboard focus and event propagation
|
||||||
|
|
||||||
TODO.
|
TODO.
|
||||||
|
|
|
@ -125,20 +125,13 @@ static jwidget_poly type_jcounter = {
|
||||||
|
|
||||||
static int jcounter_type_id;
|
static int jcounter_type_id;
|
||||||
|
|
||||||
__attribute__((constructor(2001)))
|
__attribute__((constructor))
|
||||||
static void j_register_jcounter(void)
|
static void j_register_jcounter(void)
|
||||||
{
|
{
|
||||||
jcounter_type_id = j_register_widget(&type_jcounter, "jwidget");
|
jcounter_type_id = j_register_widget(&type_jcounter);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The second parameter to `j_register_widget()` specifies inheritance. `jcounter`
|
|
||||||
inherits from `jwidget`, which means that the unspecified polymorphic functions
|
|
||||||
(`layout`, `event` and `destroy`) will use the default behavior of `jwidget`.
|
|
||||||
This is mostly useful if you don't specify `csize` (the default behavior is to
|
|
||||||
select the smallest size where all children fit) or `render` (the default
|
|
||||||
behavior is to render all visible children).
|
|
||||||
|
|
||||||
The type ID returned by `j_register_widget()` is how JustUI differentiates
|
The type ID returned by `j_register_widget()` is how JustUI differentiates
|
||||||
labels from input fields from custom counters. When creating the widget, you
|
labels from input fields from custom counters. When creating the widget, you
|
||||||
should initialize the `jwidget` field with `jwidget_init()` and specify the
|
should initialize the `jwidget` field with `jwidget_init()` and specify the
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <justui/jwidget.h>
|
#include <justui/jwidget.h>
|
||||||
|
|
||||||
#include <gint/keyboard.h>
|
#include <gint/keyboard.h>
|
||||||
|
#include <gint/defs/attributes.h>
|
||||||
|
|
||||||
/* jevent: GUI event
|
/* jevent: GUI event
|
||||||
|
|
||||||
|
@ -49,4 +50,20 @@ typedef struct {
|
||||||
|
|
||||||
} jevent;
|
} jevent;
|
||||||
|
|
||||||
|
/* Check if an event is a key press of the specified key with modifiers. */
|
||||||
|
GINLINE static bool jevent_is_press_mods(
|
||||||
|
jevent e, int key, bool shift, bool alpha) {
|
||||||
|
return e.type == JWIDGET_KEY
|
||||||
|
&& (e.key.type == KEYEV_DOWN || e.key.type == KEYEV_HOLD)
|
||||||
|
&& e.key.key == key
|
||||||
|
&& e.key.shift == shift
|
||||||
|
&& e.key.alpha == alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define jevent_is_press(E, KEY) jevent_is_press_mods(E, KEY, false, false)
|
||||||
|
#define jevent_is_shift_press(E, KEY) jevent_is_press_mods(E, KEY, true, false)
|
||||||
|
#define jevent_is_alpha_press(E, KEY) jevent_is_press_mods(E, KEY, false, true)
|
||||||
|
#define jevent_is_shift_alpha_press(E, KEY) \
|
||||||
|
jevent_is_press_mods(E, KEY, true, true)
|
||||||
|
|
||||||
#endif /* _J_EVENT */
|
#endif /* _J_EVENT */
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
* "/NAME" for a menu key;
|
* "/NAME" for a menu key;
|
||||||
* ".NAME" for an entry key;
|
* ".NAME" for an entry key;
|
||||||
* "@NAME" for an action key;
|
* "@NAME" for an action key;
|
||||||
" "#NAME" for a special key.
|
* "#NAME" for a special key.
|
||||||
|
|
||||||
The names are separated by semicolons, eg. "/F1;;/F3;.F4;@F5;#F6". Several
|
The names are separated by semicolons, eg. "/F1;;/F3;.F4;@F5;#F6". Several
|
||||||
sets of function keys can be defined if separated by a '|' character. For
|
sets of function keys can be defined if separated by a '|' character. For
|
||||||
|
@ -51,7 +51,11 @@
|
||||||
with its 128x64 resolution, the convention is that the image is 128x8, and
|
with its 128x64 resolution, the convention is that the image is 128x8, and
|
||||||
key #i is positioned at x = 21i+2 with width 19. The equivalent of "|"-
|
key #i is positioned at x = 21i+2 with width 19. The equivalent of "|"-
|
||||||
separated levels is allowed by stacking up rows of keys (in which case the
|
separated levels is allowed by stacking up rows of keys (in which case the
|
||||||
image is of height 9n-1 for n rows). */
|
image is of height 9n-1 for n rows).
|
||||||
|
|
||||||
|
jkfeys will gobble keyboard events for F1..F6 and emit JFKEYS_TRIGGERED
|
||||||
|
events instead. However, in general jfkeys doesn't have keyboard focus, so
|
||||||
|
you have to give the events manually. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
jwidget widget;
|
jwidget widget;
|
||||||
int8_t level;
|
int8_t level;
|
||||||
|
@ -72,6 +76,9 @@ typedef struct {
|
||||||
|
|
||||||
} jfkeys;
|
} jfkeys;
|
||||||
|
|
||||||
|
/* Events */
|
||||||
|
extern uint16_t JFKEYS_TRIGGERED;
|
||||||
|
|
||||||
/* jfkeys_create2(): Create a set of function keys
|
/* jfkeys_create2(): Create a set of function keys
|
||||||
|
|
||||||
Both the image and text specification are provided; one of them should
|
Both the image and text specification are provided; one of them should
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
#include <justui/jwidget.h>
|
#include <justui/jwidget.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
/* Selected item is styled by the paint function or delegate */
|
||||||
|
JLIST_SELECTION_MANUAL = 0,
|
||||||
/* Selected item is indicated by inverting its rendered area */
|
/* Selected item is indicated by inverting its rendered area */
|
||||||
JLIST_SELECTION_INVERT = 0,
|
JLIST_SELECTION_INVERT = 1,
|
||||||
/* Selected item is indicated by applying a background color */
|
/* Selected item is indicated by applying a background color */
|
||||||
JLIST_SELECTION_BACKGROUND = 1,
|
JLIST_SELECTION_BACKGROUND = 2,
|
||||||
|
|
||||||
} jlist_selection_style;
|
} jlist_selection_style;
|
||||||
|
|
||||||
|
@ -23,6 +25,10 @@ typedef struct {
|
||||||
bool selectable;
|
bool selectable;
|
||||||
/* Whether item can be triggered */
|
/* Whether item can be triggered */
|
||||||
bool triggerable;
|
bool triggerable;
|
||||||
|
/* Selection style for jlist to draw */
|
||||||
|
int8_t selection_style;
|
||||||
|
/* Selection background color for JLIST_SELECTION_BACKGROUND */
|
||||||
|
uint16_t selection_bg_color;
|
||||||
|
|
||||||
/* The following fields are only applicable if there is no delegate. */
|
/* The following fields are only applicable if there is no delegate. */
|
||||||
|
|
||||||
|
@ -33,9 +39,16 @@ typedef struct {
|
||||||
|
|
||||||
struct jlist;
|
struct jlist;
|
||||||
|
|
||||||
|
/* Info function: should fill `info` with the data related to list element
|
||||||
|
#index (starts at 0). `info` is guaranteed to be pre-initialized to 0. */
|
||||||
typedef void (*jlist_item_info_function)(struct jlist *list, int index,
|
typedef void (*jlist_item_info_function)(struct jlist *list, int index,
|
||||||
jlist_item_info *info);
|
jlist_item_info *info);
|
||||||
|
|
||||||
|
/* Paint function: should draw element #index on the rectangle of size `w×h`
|
||||||
|
at position `x,y`. If the item has a selection style that is not
|
||||||
|
JLIST_SELECTION_MANUAL, the selection effect is handled by jlist. Otherwise,
|
||||||
|
the paint function should check the `selected` parameter to apply any
|
||||||
|
relevant styling. */
|
||||||
typedef void (*jlist_item_paint_function)(int x, int y, int w, int h,
|
typedef void (*jlist_item_paint_function)(int x, int y, int w, int h,
|
||||||
struct jlist *list, int index, bool selected);
|
struct jlist *list, int index, bool selected);
|
||||||
|
|
||||||
|
@ -64,6 +77,8 @@ typedef struct jlist {
|
||||||
|
|
||||||
/* Currently selected item, -1 if none */
|
/* Currently selected item, -1 if none */
|
||||||
int cursor;
|
int cursor;
|
||||||
|
/* User data pointer */
|
||||||
|
void *user;
|
||||||
|
|
||||||
} jlist;
|
} jlist;
|
||||||
|
|
||||||
|
@ -73,13 +88,14 @@ extern uint16_t JLIST_SELECTION_MOVED;
|
||||||
extern uint16_t JLIST_MODEL_UPDATED;
|
extern uint16_t JLIST_MODEL_UPDATED;
|
||||||
|
|
||||||
/* jlist_create(): Create a new (empty) jlist. */
|
/* jlist_create(): Create a new (empty) jlist. */
|
||||||
jlist *jlist_create(void *parent, jlist_item_info_function info_function,
|
jlist *jlist_create(jlist_item_info_function info_function,
|
||||||
jlist_item_paint_function paint_function);
|
jlist_item_paint_function paint_function, void *parent);
|
||||||
|
|
||||||
/* jlist_update_model(): Update jlists's information about the model
|
/* jlist_update_model(): Update jlists's information about the model
|
||||||
The new model size is passed as parameter. The model is refreshed by
|
The new model size is passed as parameter. The model is refreshed by
|
||||||
repeatedly calling the info function. */
|
repeatedly calling the info function. The user pointer is also updated. To
|
||||||
void jlist_update_model(jlist *l, int item_count);
|
keep it unchanged, pass `l->user` as third parameter. */
|
||||||
|
void jlist_update_model(jlist *l, int item_count, void *user);
|
||||||
|
|
||||||
/* jlist_clear(): Remove all items */
|
/* jlist_clear(): Remove all items */
|
||||||
void jlist_clear(jlist *l);
|
void jlist_clear(jlist *l);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include <justui/defs.h>
|
#include <justui/defs.h>
|
||||||
#include <justui/jwidget.h>
|
#include <justui/jwidget.h>
|
||||||
#include <justui/jwidget-api.h>
|
|
||||||
|
|
||||||
/* jpainted: Simple widget designed to integrate low-effort rendering
|
/* jpainted: Simple widget designed to integrate low-effort rendering
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,8 @@ typedef struct {
|
||||||
bool mainmenu;
|
bool mainmenu;
|
||||||
/* Whether jscene_run() powers off */
|
/* Whether jscene_run() powers off */
|
||||||
bool poweroff;
|
bool poweroff;
|
||||||
|
/* Whether jscene_run() will autopaint */
|
||||||
|
bool autopaint;
|
||||||
|
|
||||||
} jscene;
|
} jscene;
|
||||||
|
|
||||||
|
@ -118,6 +120,17 @@ void jscene_set_mainmenu(jscene *scene, bool mainmenu);
|
||||||
will probably want to save important data before leaving anyway. */
|
will probably want to save important data before leaving anyway. */
|
||||||
void jscene_set_poweroff(jscene *scene, bool poweroff);
|
void jscene_set_poweroff(jscene *scene, bool poweroff);
|
||||||
|
|
||||||
|
/* jscene_set_autopaint(): Set whether jscene_run() handles its own painting
|
||||||
|
|
||||||
|
This will automatically handle JSCENE_PAINT events by drawing the scene
|
||||||
|
widget and updating the screen. You should use this only if the scene is the
|
||||||
|
only thing to draw; don't overdraw after this. If you have things to draw
|
||||||
|
not handled by jscene, handle JSCENE_PAINT yourself.
|
||||||
|
|
||||||
|
When enabling autopaint, you should also set a background color for the
|
||||||
|
scene, otherwise frames will draw transparently on top of each other. */
|
||||||
|
void jscene_set_autopaint(jscene *scene, bool autopaint);
|
||||||
|
|
||||||
/* jscene_run(): Run a scene's main loop
|
/* jscene_run(): Run a scene's main loop
|
||||||
|
|
||||||
This function implements a main control loop that sleeps when there is
|
This function implements a main control loop that sleeps when there is
|
||||||
|
|
|
@ -25,9 +25,10 @@ typedef struct {
|
||||||
|
|
||||||
} jscrolledlist;
|
} jscrolledlist;
|
||||||
|
|
||||||
/* jscrolledlist_create(): Create a scrolled list */
|
/* Create a scrolled list; arguments are forwarded to the jlist. */
|
||||||
jscrolledlist *jscrolledlist_create(void *parent,
|
jscrolledlist *jscrolledlist_create(
|
||||||
jlist_item_info_function info_function,
|
jlist_item_info_function info_function,
|
||||||
jlist_item_paint_function paint_function);
|
jlist_item_paint_function paint_function,
|
||||||
|
void *parent);
|
||||||
|
|
||||||
#endif /* _J_JSCROLLEDLIST */
|
#endif /* _J_JSCROLLEDLIST */
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define _J_JWIDGET_API
|
#define _J_JWIDGET_API
|
||||||
|
|
||||||
#include <justui/defs.h>
|
#include <justui/defs.h>
|
||||||
|
#include <justui/p/preproc.h>
|
||||||
#include <justui/jevent.h>
|
#include <justui/jevent.h>
|
||||||
#include <gint/keyboard.h>
|
#include <gint/keyboard.h>
|
||||||
|
|
||||||
|
@ -28,7 +29,13 @@
|
||||||
|
|
||||||
Implementations of this function should use jwidget_msize() on the children
|
Implementations of this function should use jwidget_msize() on the children
|
||||||
to position their margin box within the widget's content box. The size set
|
to position their margin box within the widget's content box. The size set
|
||||||
by this function needs not be in the minimum/maximum range of the widget. */
|
by this function needs not be in the minimum/maximum range of the widget,
|
||||||
|
which is handled later.
|
||||||
|
|
||||||
|
If not overloaded (i.e. NULL in the poly structure), the default behavior is
|
||||||
|
to compute the smallest size that fits all children (based on their own
|
||||||
|
natural content size), which only makes sense if the children are at fixed
|
||||||
|
positions. */
|
||||||
typedef void jwidget_poly_csize_t(void *w);
|
typedef void jwidget_poly_csize_t(void *w);
|
||||||
|
|
||||||
/* jwidget_poly_layout_t: Layout a widget after its size has been set
|
/* jwidget_poly_layout_t: Layout a widget after its size has been set
|
||||||
|
@ -37,25 +44,45 @@ typedef void jwidget_poly_csize_t(void *w);
|
||||||
the widget has no layout. The margin-box size allocated to the widget has
|
the widget has no layout. The margin-box size allocated to the widget has
|
||||||
been set in (w->w) and (w->h); the widget must now position its contents and
|
been set in (w->w) and (w->h); the widget must now position its contents and
|
||||||
children. If the widget has a layout, the layout's specialized function is
|
children. If the widget has a layout, the layout's specialized function is
|
||||||
called instead of this one. */
|
called instead of this one.
|
||||||
|
|
||||||
|
Custom positioning for children is only relevant for widgets that use custom
|
||||||
|
layouts, which is fairly rare. Most often, this function is used to position
|
||||||
|
internal elements of the widget after the size has been set (e.g. jlabel
|
||||||
|
computes line breaks here).
|
||||||
|
|
||||||
|
If not overloaded (i.e. NULL in the poly structure), the default behavior is
|
||||||
|
to leave children's positions unchanged, assuming they are fixed. */
|
||||||
typedef void jwidget_poly_layout_t(void *w);
|
typedef void jwidget_poly_layout_t(void *w);
|
||||||
|
|
||||||
/* jwidget_poly_render_t: Render a widget
|
/* jwidget_poly_render_t: Render a widget
|
||||||
|
|
||||||
This function is called during rendering after the widget's geometry is
|
This function is called during rendering after the widget's geometry is
|
||||||
drawn. (x,y) are the coordinates of the content box. This function must
|
drawn. (x,y) are the coordinates of the content box. This function must
|
||||||
render widget-specific visuals; there is no clipping, so the widget should
|
render widget-specific visuals. If the widget is clipped (as specified by
|
||||||
honor its width and height.
|
`jwidget_set_clipped()`), this function can specify any drawing coordinates
|
||||||
|
and all drawing will automatically be restricted with the widget's box.
|
||||||
|
However, if the widget is not clipped, drawing beyond the widget's width and
|
||||||
|
height will overflow to other widgets.
|
||||||
|
|
||||||
This function should call jwidget_render() for all children that need to be
|
This function should render its children. In the simple case where all
|
||||||
rendered. jwidget_render() handles the geometry and takes as parameters the
|
children can be rendered at the same time, you can simply call
|
||||||
coordinates of the margin box, so it can be called as:
|
`jwidget_poly_render(w)` which will do that.
|
||||||
|
|
||||||
|
If for any reason children need to be rendered separately, this function can
|
||||||
|
also called `jwidget_render()` on individual children. `jwidget_render()`
|
||||||
|
handles the geometry and takes as parameters the coordinates of the margin
|
||||||
|
box, so it can be called as:
|
||||||
|
|
||||||
jwidget_render(child, x + child->x, y + child->y).
|
jwidget_render(child, x + child->x, y + child->y).
|
||||||
|
|
||||||
It will draw the geometry and call the polymorphic renderer of the child at
|
It will draw the geometry and call the polymorphic renderer of the child at
|
||||||
its content-box coordinates. Normally you can ignore geometry altogether. */
|
its content-box coordinates. Normally you can ignore geometry altogether.
|
||||||
|
|
||||||
|
If not overloaded, the behavior is `jwidget_poly_render(w)`, i.e. just
|
||||||
|
render the children if there are any. */
|
||||||
typedef void jwidget_poly_render_t(void *w, int x, int y);
|
typedef void jwidget_poly_render_t(void *w, int x, int y);
|
||||||
|
extern jwidget_poly_render_t jwidget_poly_render;
|
||||||
|
|
||||||
/* jwidget_poly_event_t: Handle an event
|
/* jwidget_poly_event_t: Handle an event
|
||||||
|
|
||||||
|
@ -63,22 +90,32 @@ typedef void jwidget_poly_render_t(void *w, int x, int y);
|
||||||
key events. This function is somewhat of a catch-all function for dynamic
|
key events. This function is somewhat of a catch-all function for dynamic
|
||||||
occurrences. The widget should either accept the event, do something with
|
occurrences. The widget should either accept the event, do something with
|
||||||
it, and return true, or refuse the event, do nothing and return false. This
|
it, and return true, or refuse the event, do nothing and return false. This
|
||||||
influences events that propagate, such as key events. */
|
influences events that propagate, such as key events.
|
||||||
|
|
||||||
|
This function should always default to
|
||||||
|
|
||||||
|
return jwidget_poly_event(w, e);
|
||||||
|
|
||||||
|
to allow default/generic behaviors to propagate, unless the widget
|
||||||
|
explicitly wants to interfere with them. */
|
||||||
typedef bool jwidget_poly_event_t(void *w, jevent e);
|
typedef bool jwidget_poly_event_t(void *w, jevent e);
|
||||||
|
extern jwidget_poly_event_t jwidget_poly_event;
|
||||||
|
|
||||||
/* jwidget_poly_destroy_t: Destroy a widget's specific resources
|
/* jwidget_poly_destroy_t: Destroy a widget's specific resources
|
||||||
|
|
||||||
This function must destroy the widget-specific resources. It is called by
|
This function must destroy the widget-specific resources. It is called by
|
||||||
jwidget_destroy(), which follows it by freeing the widget's standard data
|
jwidget_destroy(), which follows it by freeing the widget's standard data
|
||||||
and destroying the children. This function can be NULL if there are no
|
and destroying the children. This function can be NULL if there are no
|
||||||
widget-specific resources to free. */
|
widget-specific resources to free. It should not call the "base" version of
|
||||||
|
the function. */
|
||||||
typedef void jwidget_poly_destroy_t(void *w);
|
typedef void jwidget_poly_destroy_t(void *w);
|
||||||
|
|
||||||
/* jwidget_poly: Polymorphic interface for a widget type */
|
/* jwidget_poly: Polymorphic interface for a widget type */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* Type name, used for display and inheritance */
|
/* Type name, used for display and inheritance */
|
||||||
char const *name;
|
char const *name;
|
||||||
/* Polymorphic functions */
|
/* Polymorphic functions. If unused for custom widgets, should be set to
|
||||||
|
NULL and the default behavior (described above) will apply. */
|
||||||
jwidget_poly_csize_t *csize;
|
jwidget_poly_csize_t *csize;
|
||||||
jwidget_poly_layout_t *layout;
|
jwidget_poly_layout_t *layout;
|
||||||
jwidget_poly_render_t *render;
|
jwidget_poly_render_t *render;
|
||||||
|
@ -95,13 +132,8 @@ typedef struct {
|
||||||
|
|
||||||
This function returns a new widget type ID to pass to jwidget_init() when
|
This function returns a new widget type ID to pass to jwidget_init() when
|
||||||
creating widgets of the custom type. Returns -1 if registration fails. The
|
creating widgets of the custom type. Returns -1 if registration fails. The
|
||||||
polymorphic structure must outlive all widgets of the custom type.
|
polymorphic structure must outlive all widgets of the custom type. */
|
||||||
|
int j_register_widget(jwidget_poly *poly);
|
||||||
If (inherits) is non-NULL, the new type will inherit from the widget type
|
|
||||||
with the provided name. All NULL functions in (poly) will be replaced by the
|
|
||||||
parent type's functions. This mechanism only works if widgets are declared
|
|
||||||
in inheritance order, which is normally enforced by constructor priority. */
|
|
||||||
int j_register_widget(jwidget_poly *poly, char const *inherits);
|
|
||||||
|
|
||||||
/* j_register_event(): Register a new event type
|
/* j_register_event(): Register a new event type
|
||||||
|
|
||||||
|
@ -155,4 +187,52 @@ void jwidget_emit(void *w, jevent e);
|
||||||
notify it of the specified event. */
|
notify it of the specified event. */
|
||||||
bool jwidget_event(void *w, jevent e);
|
bool jwidget_event(void *w, jevent e);
|
||||||
|
|
||||||
|
/* Helper macro for defining a new widget. Parameters are:
|
||||||
|
- Widget NAME
|
||||||
|
- Variadic list of overridden widget poly METHODs, can be empty
|
||||||
|
- Implicity, non-static poly functions `<NAME>_poly_<METHOD>`
|
||||||
|
And it defines:
|
||||||
|
- The `NAME_type_id` identifier for use with `jwidget_init()`
|
||||||
|
- A constructor that registers the widget and sets `NAME_type_id`.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
```
|
||||||
|
J_DEFINE_WIDGET(jpainted, csize, render)
|
||||||
|
```
|
||||||
|
|
||||||
|
gets the functions `jpainted_poly_csize` and `jpainted_poly_render` from
|
||||||
|
context, registers the type at startup and provides `jpainted_type_id` for
|
||||||
|
the widget creation function. */
|
||||||
|
#define J_DEFINE_WIDGET(NAME, ...) \
|
||||||
|
static int NAME##_type_id = -1; \
|
||||||
|
J_REPEAT(J_DEFINE_WIDGET_POLY_PROTO, (NAME), __VA_ARGS__) \
|
||||||
|
static jwidget_poly type_##NAME = { \
|
||||||
|
.name = #NAME, \
|
||||||
|
J_REPEAT(J_DEFINE_WIDGET_POLY, (NAME), __VA_ARGS__) \
|
||||||
|
}; \
|
||||||
|
__attribute__((constructor)) \
|
||||||
|
static void j_register_##NAME(void) { \
|
||||||
|
NAME##_type_id = j_register_widget(&type_##NAME); \
|
||||||
|
}
|
||||||
|
#define J_DEFINE_WIDGET_POLY(NAME, METHOD) \
|
||||||
|
.METHOD = NAME##_poly_##METHOD,
|
||||||
|
#define J_DEFINE_WIDGET_POLY_PROTO(NAME, METHOD) \
|
||||||
|
extern jwidget_poly_##METHOD##_t NAME##_poly_##METHOD;
|
||||||
|
|
||||||
|
/* Helper macro for defining new events. Each parameter is an event name to
|
||||||
|
define, e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
J_DEFINE_EVENTS(MYWIDGET_EVENT1, MYWIDGET_EVENT2, MYWIDGET_EVENT3)
|
||||||
|
``` */
|
||||||
|
#define J_DEFINE_EVENTS(...) \
|
||||||
|
uint16_t __VA_ARGS__; \
|
||||||
|
__attribute__((constructor)) \
|
||||||
|
static void J_DEFINE_EVENTS_NAME(__COUNTER__)(void) { \
|
||||||
|
J_REPEAT(J_DEFINE_EVENTS_INIT, (), __VA_ARGS__) \
|
||||||
|
}
|
||||||
|
#define J_DEFINE_EVENTS_NAME2(COUNTER) _j_init_##COUNTER
|
||||||
|
#define J_DEFINE_EVENTS_NAME(COUNTER) J_DEFINE_EVENTS_NAME2(COUNTER)
|
||||||
|
#define J_DEFINE_EVENTS_INIT(EVENT) EVENT = j_register_event();
|
||||||
|
|
||||||
#endif /* _J_JWIDGET_API */
|
#endif /* _J_JWIDGET_API */
|
||||||
|
|
64
include/justui/p/preproc.h
Normal file
64
include/justui/p/preproc.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
//---
|
||||||
|
// JustUI.util.preproc: Preprocessor utilities
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef _J_UTIL_PREPROC
|
||||||
|
#define _J_UTIL_PREPROC
|
||||||
|
|
||||||
|
/* J_REPEAT: Dispatch a partially-evaluated macro call on each arg in a list
|
||||||
|
The call `J_REPEAT(X, C, V1, ..., VN)` where `C = (C1, ..., CM)` (with the
|
||||||
|
parentheses) is equivalent to the series of calls
|
||||||
|
|
||||||
|
```
|
||||||
|
X(C1, ..., CM, V1)
|
||||||
|
X(C1, ..., CM, V2)
|
||||||
|
...
|
||||||
|
X(C1, ..., CM, VN)
|
||||||
|
```
|
||||||
|
|
||||||
|
and is used to iterate on lists. There is a size limit of N≤8. */
|
||||||
|
#define J_REPEAT1(X, C, V1, ...) \
|
||||||
|
J_CALL(X, C, V1) __VA_OPT__(J_REPEAT2(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT2(X, C, V2, ...) \
|
||||||
|
J_CALL(X, C, V2) __VA_OPT__(J_REPEAT3(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT3(X, C, V3, ...) \
|
||||||
|
J_CALL(X, C, V3) __VA_OPT__(J_REPEAT4(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT4(X, C, V4, ...) \
|
||||||
|
J_CALL(X, C, V4) __VA_OPT__(J_REPEAT5(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT5(X, C, V5, ...) \
|
||||||
|
J_CALL(X, C, V5) __VA_OPT__(J_REPEAT6(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT6(X, C, V6, ...) \
|
||||||
|
J_CALL(X, C, V6) __VA_OPT__(J_REPEAT7(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT7(X, C, V7, ...) \
|
||||||
|
J_CALL(X, C, V7) __VA_OPT__(J_REPEAT8(X, C, __VA_ARGS__))
|
||||||
|
#define J_REPEAT8(X, C, V8, ...) \
|
||||||
|
({ __VA_OPT__(_Static_assert(0, \
|
||||||
|
"J_REPEAT: too many macro arguments (maximum 8)");) \
|
||||||
|
J_CALL(X, C, V8); })
|
||||||
|
#define J_REPEAT(X, C, ...) __VA_OPT__(J_REPEAT1(X, C, __VA_ARGS__))
|
||||||
|
|
||||||
|
/* J_CALL: Perform a call to a partially evaluated macro
|
||||||
|
The call `J_CALL(X, C, A1, ..., AN)` where `C = (C1, ..., CM)` (with the
|
||||||
|
parentheses) reduces to `X(C1, ..., CM, A1, ..., AN)`, i.e. it calls the
|
||||||
|
already-partially-applied `X(C)` with further arguments. Both M=0 (`C=()`)
|
||||||
|
and N=0 (no variadic arguments) are allowed.
|
||||||
|
|
||||||
|
The main difficulty is "unfolding" `C` into the arguments of `X`. This
|
||||||
|
problem is dealt with by absorbing the parentheses into an ID-function macro
|
||||||
|
call:
|
||||||
|
|
||||||
|
```
|
||||||
|
J_ID C
|
||||||
|
~> J_ID (C1, ..., CM)
|
||||||
|
~> , C1, ..., CM
|
||||||
|
```
|
||||||
|
|
||||||
|
From there, the only subtletly is gobbling the commas. We gobble the comma
|
||||||
|
before `J_ID C` if M=0 and the comma before the other arguments if N=0. This
|
||||||
|
requires a few expansion stages. */
|
||||||
|
#define J_ID(...) __VA_OPT__(,) __VA_ARGS__
|
||||||
|
#define J_CALL3(X, ...) X(__VA_ARGS__)
|
||||||
|
#define J_CALL2(...) J_CALL3(__VA_ARGS__)
|
||||||
|
#define J_CALL(X, C, ...) J_CALL2(X J_ID C, ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#endif /* _J_UTIL_PREPROC */
|
|
@ -567,7 +567,7 @@ static bool jfileselect_poly_event(void *fs0, jevent e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return jwidget_poly_event(fs, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jfileselect_poly_destroy(void *fs0)
|
static void jfileselect_poly_destroy(void *fs0)
|
||||||
|
@ -589,10 +589,10 @@ static jwidget_poly type_jfileselect = {
|
||||||
.destroy = jfileselect_poly_destroy,
|
.destroy = jfileselect_poly_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1003)))
|
__attribute__((constructor))
|
||||||
static void j_register_jfileselect(void)
|
static void j_register_jfileselect(void)
|
||||||
{
|
{
|
||||||
jfileselect_type_id = j_register_widget(&type_jfileselect, "jwidget");
|
jfileselect_type_id = j_register_widget(&type_jfileselect);
|
||||||
JFILESELECT_LOADED = j_register_event();
|
JFILESELECT_LOADED = j_register_event();
|
||||||
JFILESELECT_VALIDATED = j_register_event();
|
JFILESELECT_VALIDATED = j_register_event();
|
||||||
JFILESELECT_CANCELED = j_register_event();
|
JFILESELECT_CANCELED = j_register_event();
|
||||||
|
|
64
src/jfkeys.c
64
src/jfkeys.c
|
@ -4,8 +4,8 @@
|
||||||
#include <gint/std/stdlib.h>
|
#include <gint/std/stdlib.h>
|
||||||
#include <gint/std/string.h>
|
#include <gint/std/string.h>
|
||||||
|
|
||||||
/* Type identified for jfkeys */
|
J_DEFINE_WIDGET(jfkeys, csize, render, event)
|
||||||
static int jfkeys_type_id = -1;
|
J_DEFINE_EVENTS(JFKEYS_TRIGGERED)
|
||||||
|
|
||||||
extern font_t j_font_fkeys_fx;
|
extern font_t j_font_fkeys_fx;
|
||||||
|
|
||||||
|
@ -89,14 +89,14 @@ static char const *get_label(char const *level, int key, size_t *len)
|
||||||
// Polymorphic widget operations
|
// Polymorphic widget operations
|
||||||
//---
|
//---
|
||||||
|
|
||||||
static void jfkeys_poly_csize(void *f0)
|
void jfkeys_poly_csize(void *f0)
|
||||||
{
|
{
|
||||||
jfkeys *f = f0;
|
jfkeys *f = f0;
|
||||||
f->widget.w = DWIDTH;
|
f->widget.w = DWIDTH;
|
||||||
f->widget.h = JFKEYS_HEIGHT;
|
f->widget.h = JFKEYS_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jfkeys_poly_render(void *f0, int base_x, int y)
|
void jfkeys_poly_render(void *f0, int base_x, int y)
|
||||||
{
|
{
|
||||||
jfkeys *f = f0;
|
jfkeys *f = f0;
|
||||||
|
|
||||||
|
@ -117,8 +117,11 @@ static void jfkeys_poly_render(void *f0, int base_x, int y)
|
||||||
if(!text || (*text != '.' && *text != '/' && *text != '@'
|
if(!text || (*text != '.' && *text != '/' && *text != '@'
|
||||||
&& *text != '#')) continue;
|
&& *text != '#')) continue;
|
||||||
|
|
||||||
int x = base_x + 4 + 65 * position;
|
int fw = jwidget_full_width(f);
|
||||||
int w = 63;
|
int margin = (fw >= 250 ? 4 : 2);
|
||||||
|
int spacing = 2;
|
||||||
|
int w = (fw - 2 * margin - 5 * spacing) / 6;
|
||||||
|
int x = base_x + margin + (w + spacing) * position;
|
||||||
int color = (text[0] == '#') ? f->text_special_color : f->text_color;
|
int color = (text[0] == '#') ? f->text_special_color : f->text_color;
|
||||||
|
|
||||||
if(text[0] == '.') {
|
if(text[0] == '.') {
|
||||||
|
@ -153,6 +156,38 @@ static void jfkeys_poly_render(void *f0, int base_x, int y)
|
||||||
dfont(old_font);
|
dfont(old_font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jfkeys_poly_event(void *f0, jevent e)
|
||||||
|
{
|
||||||
|
jfkeys *f = f0;
|
||||||
|
jevent te;
|
||||||
|
te.source = f;
|
||||||
|
te.type = JFKEYS_TRIGGERED;
|
||||||
|
|
||||||
|
if(e.type == JWIDGET_KEY && e.key.type == KEYEV_DOWN) {
|
||||||
|
int fun = keycode_function(e.key.key);
|
||||||
|
if(fun >= 0) {
|
||||||
|
te.data = fun - 1;
|
||||||
|
jwidget_emit(f, te);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if GINT_HW_CP
|
||||||
|
static uint8_t const CP_Fk[6] = {
|
||||||
|
KEY_EQUALS, KEY_X, KEY_Y, KEY_Z, KEY_CARET, KEY_DIV };
|
||||||
|
|
||||||
|
for(int i = 0; i < 6; i++) {
|
||||||
|
if(e.key.key == CP_Fk[i]) {
|
||||||
|
te.data = i;
|
||||||
|
jwidget_emit(f, te);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return jwidget_poly_event(f, e);
|
||||||
|
}
|
||||||
|
|
||||||
int jfkeys_level(jfkeys *f)
|
int jfkeys_level(jfkeys *f)
|
||||||
{
|
{
|
||||||
return f->level;
|
return f->level;
|
||||||
|
@ -190,20 +225,3 @@ void jfkeys_set_font(jfkeys *keys, font_t const *font)
|
||||||
{
|
{
|
||||||
keys->font = font;
|
keys->font = font;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* jfkeys type definition */
|
|
||||||
static jwidget_poly type_jfkeys = {
|
|
||||||
.name = "jfkeys",
|
|
||||||
.csize = jfkeys_poly_csize,
|
|
||||||
.layout = NULL,
|
|
||||||
.render = jfkeys_poly_render,
|
|
||||||
.event = NULL,
|
|
||||||
.destroy = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Type registration */
|
|
||||||
__attribute__((constructor(1005)))
|
|
||||||
static void j_register_jfkeys(void)
|
|
||||||
{
|
|
||||||
jfkeys_type_id = j_register_widget(&type_jfkeys, "jwidget");
|
|
||||||
}
|
|
||||||
|
|
|
@ -379,7 +379,7 @@ static bool jframe_poly_event(void *f0, jevent e)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return jwidget_poly_event(f, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* jframe type definition */
|
/* jframe type definition */
|
||||||
|
@ -391,8 +391,8 @@ static jwidget_poly type_jframe = {
|
||||||
.event = jframe_poly_event,
|
.event = jframe_poly_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1001)))
|
__attribute__((constructor))
|
||||||
static void j_register_jframe(void)
|
static void j_register_jframe(void)
|
||||||
{
|
{
|
||||||
jframe_type_id = j_register_widget(&type_jframe, "jwidget");
|
jframe_type_id = j_register_widget(&type_jframe);
|
||||||
}
|
}
|
||||||
|
|
11
src/jinput.c
11
src/jinput.c
|
@ -256,6 +256,7 @@ static bool jinput_poly_event(void *i0, jevent e)
|
||||||
|
|
||||||
if(e.type == JWIDGET_KEY) {
|
if(e.type == JWIDGET_KEY) {
|
||||||
key_event_t ev = e.key;
|
key_event_t ev = e.key;
|
||||||
|
bool handled = true;
|
||||||
|
|
||||||
/* Releasing modifiers */
|
/* Releasing modifiers */
|
||||||
if(ev.type == KEYEV_UP && ev.key == KEY_SHIFT) {
|
if(ev.type == KEYEV_UP && ev.key == KEY_SHIFT) {
|
||||||
|
@ -326,13 +327,15 @@ static bool jinput_poly_event(void *i0, jevent e)
|
||||||
/* Remove modifiers otherwise */
|
/* Remove modifiers otherwise */
|
||||||
else i->mode &= ~(JINPUT_SHIFT | JINPUT_ALPHA);
|
else i->mode &= ~(JINPUT_SHIFT | JINPUT_ALPHA);
|
||||||
}
|
}
|
||||||
else return false;
|
else handled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
i->widget.update = 1;
|
i->widget.update = 1;
|
||||||
|
if(handled)
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return jwidget_poly_event(i, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jinput_poly_destroy(void *i0)
|
static void jinput_poly_destroy(void *i0)
|
||||||
|
@ -351,10 +354,10 @@ static jwidget_poly type_jinput = {
|
||||||
.destroy = jinput_poly_destroy,
|
.destroy = jinput_poly_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1003)))
|
__attribute__((constructor))
|
||||||
static void j_register_jinput(void)
|
static void j_register_jinput(void)
|
||||||
{
|
{
|
||||||
jinput_type_id = j_register_widget(&type_jinput, "jwidget");
|
jinput_type_id = j_register_widget(&type_jinput);
|
||||||
JINPUT_VALIDATED = j_register_event();
|
JINPUT_VALIDATED = j_register_event();
|
||||||
JINPUT_CANCELED = j_register_event();
|
JINPUT_CANCELED = j_register_event();
|
||||||
}
|
}
|
||||||
|
|
|
@ -354,8 +354,8 @@ static jwidget_poly type_jlabel = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Type registration */
|
/* Type registration */
|
||||||
__attribute__((constructor(1002)))
|
__attribute__((constructor))
|
||||||
static void j_register_jlabel(void)
|
static void j_register_jlabel(void)
|
||||||
{
|
{
|
||||||
jlabel_type_id = j_register_widget(&type_jlabel, "jwidget");
|
jlabel_type_id = j_register_widget(&type_jlabel);
|
||||||
}
|
}
|
||||||
|
|
43
src/jlist.c
43
src/jlist.c
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <gint/display.h>
|
#include <gint/display.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/* Type identifier for jlist */
|
/* Type identifier for jlist */
|
||||||
static int jlist_type_id = -1;
|
static int jlist_type_id = -1;
|
||||||
|
@ -19,8 +20,8 @@ struct jlist_item_info {
|
||||||
bool selectable;
|
bool selectable;
|
||||||
};
|
};
|
||||||
|
|
||||||
jlist *jlist_create(void *parent, jlist_item_info_function info_function,
|
jlist *jlist_create(jlist_item_info_function info_function,
|
||||||
jlist_item_paint_function paint_function)
|
jlist_item_paint_function paint_function, void *parent)
|
||||||
{
|
{
|
||||||
if(jlist_type_id < 0)
|
if(jlist_type_id < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -36,6 +37,7 @@ jlist *jlist_create(void *parent, jlist_item_info_function info_function,
|
||||||
l->info_function = info_function;
|
l->info_function = info_function;
|
||||||
l->paint_function = paint_function;
|
l->paint_function = paint_function;
|
||||||
l->cursor = -1;
|
l->cursor = -1;
|
||||||
|
l->user = NULL;
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +114,10 @@ int jlist_selected_item(jlist *l)
|
||||||
// Item management
|
// Item management
|
||||||
//---
|
//---
|
||||||
|
|
||||||
void jlist_update_model(jlist *l, int item_count)
|
void jlist_update_model(jlist *l, int item_count, void *user)
|
||||||
{
|
{
|
||||||
|
l->user = user;
|
||||||
|
|
||||||
if(l->item_count != item_count) {
|
if(l->item_count != item_count) {
|
||||||
l->items = realloc(l->items, item_count * sizeof *l->items);
|
l->items = realloc(l->items, item_count * sizeof *l->items);
|
||||||
if(!l->items) {
|
if(!l->items) {
|
||||||
|
@ -126,6 +130,7 @@ void jlist_update_model(jlist *l, int item_count)
|
||||||
|
|
||||||
l->item_count = item_count;
|
l->item_count = item_count;
|
||||||
for(int i = 0; i < item_count; i++) {
|
for(int i = 0; i < item_count; i++) {
|
||||||
|
memset(&l->items[i], 0, sizeof l->items[i]);
|
||||||
l->info_function(l, i, &l->items[i]);
|
l->info_function(l, i, &l->items[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +141,7 @@ void jlist_update_model(jlist *l, int item_count)
|
||||||
|
|
||||||
void jlist_clear(jlist *l)
|
void jlist_clear(jlist *l)
|
||||||
{
|
{
|
||||||
jlist_update_model(l, 0);
|
jlist_update_model(l, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
jrect jlist_selected_region(jlist *l)
|
jrect jlist_selected_region(jlist *l)
|
||||||
|
@ -193,16 +198,24 @@ static void jlist_poly_render(void *l0, int x, int y)
|
||||||
|
|
||||||
for(int i = 0; i < l->item_count; i++) {
|
for(int i = 0; i < l->item_count; i++) {
|
||||||
jlist_item_info *info = &l->items[i];
|
jlist_item_info *info = &l->items[i];
|
||||||
|
bool selected = (l->cursor == i);
|
||||||
|
|
||||||
if(info->delegate) {
|
int h = info->delegate
|
||||||
|
? jwidget_full_height(info->delegate)
|
||||||
|
: info->natural_height;
|
||||||
|
|
||||||
|
if(selected && info->selection_style == JLIST_SELECTION_BACKGROUND)
|
||||||
|
drect(x1, y, x2, y + h - 1, info->selection_bg_color);
|
||||||
|
|
||||||
|
if(info->delegate)
|
||||||
jwidget_render(info->delegate, x1, y);
|
jwidget_render(info->delegate, x1, y);
|
||||||
y += jwidget_full_height(info->delegate);
|
else
|
||||||
}
|
l->paint_function(x1, y, x2-x1+1, h, l, i, selected);
|
||||||
else {
|
|
||||||
l->paint_function(x1, y, x2-x1+1, info->natural_height, l, i,
|
if(selected && info->selection_style == JLIST_SELECTION_INVERT)
|
||||||
l->cursor == i);
|
drect(x1, y, x2, y + h - 1, C_INVERT);
|
||||||
y += info->natural_height;
|
|
||||||
}
|
y += h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +259,7 @@ static bool jlist_poly_event(void *l0, jevent e)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return jwidget_poly_event(l, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jlist_poly_destroy(void *l0)
|
static void jlist_poly_destroy(void *l0)
|
||||||
|
@ -264,10 +277,10 @@ static jwidget_poly type_jlist = {
|
||||||
.destroy = jlist_poly_destroy,
|
.destroy = jlist_poly_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1001)))
|
__attribute__((constructor))
|
||||||
static void j_register_jlist(void)
|
static void j_register_jlist(void)
|
||||||
{
|
{
|
||||||
jlist_type_id = j_register_widget(&type_jlist, "jwidget");
|
jlist_type_id = j_register_widget(&type_jlist);
|
||||||
JLIST_ITEM_TRIGGERED = j_register_event();
|
JLIST_ITEM_TRIGGERED = j_register_event();
|
||||||
JLIST_SELECTION_MOVED = j_register_event();
|
JLIST_SELECTION_MOVED = j_register_event();
|
||||||
JLIST_MODEL_UPDATED = j_register_event();
|
JLIST_MODEL_UPDATED = j_register_event();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#include <justui/jpainted.h>
|
#include <justui/jpainted.h>
|
||||||
#include <gint/std/stdlib.h>
|
#include <justui/jwidget-api.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
/* Type identifier for jpainted */
|
J_DEFINE_WIDGET(jpainted, csize, render)
|
||||||
static int jpainted_type_id = -1;
|
|
||||||
|
|
||||||
jpainted *jpainted_create(void *function, j_arg_t arg, int natural_w,
|
jpainted *jpainted_create(void *function, j_arg_t arg, int natural_w,
|
||||||
int natural_h, void *parent)
|
int natural_h, void *parent)
|
||||||
|
@ -20,36 +20,15 @@ jpainted *jpainted_create(void *function, j_arg_t arg, int natural_w,
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---
|
void jpainted_poly_csize(void *p0)
|
||||||
// Polymorphic widget operations
|
|
||||||
//---
|
|
||||||
|
|
||||||
static void jpainted_poly_csize(void *p0)
|
|
||||||
{
|
{
|
||||||
jpainted *p = p0;
|
jpainted *p = p0;
|
||||||
p->widget.w = p->natural_w;
|
p->widget.w = p->natural_w;
|
||||||
p->widget.h = p->natural_h;
|
p->widget.h = p->natural_h;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jpainted_poly_render(void *p0, int x, int y)
|
void jpainted_poly_render(void *p0, int x, int y)
|
||||||
{
|
{
|
||||||
jpainted *p = p0;
|
jpainted *p = p0;
|
||||||
p->paint(x, y, p->arg);
|
p->paint(x, y, p->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* jpainted type definition */
|
|
||||||
static jwidget_poly type_jpainted = {
|
|
||||||
.name = "jpainted",
|
|
||||||
.csize = jpainted_poly_csize,
|
|
||||||
.layout = NULL,
|
|
||||||
.render = jpainted_poly_render,
|
|
||||||
.event = NULL,
|
|
||||||
.destroy = NULL,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Type registration */
|
|
||||||
__attribute__((constructor(1004)))
|
|
||||||
static void j_register_jpainted(void)
|
|
||||||
{
|
|
||||||
jpainted_type_id = j_register_widget(&type_jpainted, "jwidget");
|
|
||||||
}
|
|
||||||
|
|
21
src/jscene.c
21
src/jscene.c
|
@ -45,6 +45,7 @@ jscene *jscene_create(int x, int y, int w, int h, void *parent)
|
||||||
|
|
||||||
jwidget_init(&s->widget, jscene_type_id, parent);
|
jwidget_init(&s->widget, jscene_type_id, parent);
|
||||||
jwidget_set_fixed_size(s, w, h);
|
jwidget_set_fixed_size(s, w, h);
|
||||||
|
jlayout_set_vbox(s);
|
||||||
|
|
||||||
s->x = x;
|
s->x = x;
|
||||||
s->y = y;
|
s->y = y;
|
||||||
|
@ -55,6 +56,7 @@ jscene *jscene_create(int x, int y, int w, int h, void *parent)
|
||||||
s->lost_events = 0;
|
s->lost_events = 0;
|
||||||
s->mainmenu = true;
|
s->mainmenu = true;
|
||||||
s->poweroff = true;
|
s->poweroff = true;
|
||||||
|
s->autopaint = false;
|
||||||
|
|
||||||
/* Prepare first layout/paint operation */
|
/* Prepare first layout/paint operation */
|
||||||
s->widget.dirty = 1;
|
s->widget.dirty = 1;
|
||||||
|
@ -209,6 +211,16 @@ void jscene_set_mainmenu(jscene *scene, bool mainmenu)
|
||||||
scene->mainmenu = mainmenu;
|
scene->mainmenu = mainmenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void jscene_set_poweroff(jscene *scene, bool poweroff)
|
||||||
|
{
|
||||||
|
scene->poweroff = poweroff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jscene_set_autopaint(jscene *scene, bool autopaint)
|
||||||
|
{
|
||||||
|
scene->autopaint = autopaint;
|
||||||
|
}
|
||||||
|
|
||||||
jevent jscene_run(jscene *s)
|
jevent jscene_run(jscene *s)
|
||||||
{
|
{
|
||||||
keydev_t *d = keydev_std();
|
keydev_t *d = keydev_std();
|
||||||
|
@ -224,6 +236,11 @@ jevent jscene_run(jscene *s)
|
||||||
|
|
||||||
/* Queued GUI events */
|
/* Queued GUI events */
|
||||||
e = jscene_read_event(s);
|
e = jscene_read_event(s);
|
||||||
|
if(e.type == JSCENE_PAINT && s->autopaint) {
|
||||||
|
jscene_render(s);
|
||||||
|
dupdate();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(e.type != JSCENE_NONE && !jscene_process_event(s, e)) break;
|
if(e.type != JSCENE_NONE && !jscene_process_event(s, e)) break;
|
||||||
|
|
||||||
/* Queued keyboard events */
|
/* Queued keyboard events */
|
||||||
|
@ -280,10 +297,10 @@ static jwidget_poly type_jscene = {
|
||||||
.destroy = NULL,
|
.destroy = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1001)))
|
__attribute__((constructor))
|
||||||
static void j_register_jscene(void)
|
static void j_register_jscene(void)
|
||||||
{
|
{
|
||||||
jscene_type_id = j_register_widget(&type_jscene, "jwidget");
|
jscene_type_id = j_register_widget(&type_jscene);
|
||||||
JSCENE_NONE = j_register_event();
|
JSCENE_NONE = j_register_event();
|
||||||
JSCENE_PAINT = j_register_event();
|
JSCENE_PAINT = j_register_event();
|
||||||
JSCENE_KEY = JWIDGET_KEY;
|
JSCENE_KEY = JWIDGET_KEY;
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
/* Type identifier for jscrolledlist */
|
/* Type identifier for jscrolledlist */
|
||||||
static int jscrolledlist_type_id = -1;
|
static int jscrolledlist_type_id = -1;
|
||||||
|
|
||||||
jscrolledlist *jscrolledlist_create(void *parent,
|
jscrolledlist *jscrolledlist_create(
|
||||||
jlist_item_info_function info_function,
|
jlist_item_info_function info_function,
|
||||||
jlist_item_paint_function paint_function)
|
jlist_item_paint_function paint_function,
|
||||||
|
void *parent)
|
||||||
{
|
{
|
||||||
if(jscrolledlist_type_id < 0)
|
if(jscrolledlist_type_id < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -26,7 +27,7 @@ jscrolledlist *jscrolledlist_create(void *parent,
|
||||||
jwidget_set_stretch(l->frame, 1, 1, false);
|
jwidget_set_stretch(l->frame, 1, 1, false);
|
||||||
jframe_set_align(l->frame, J_ALIGN_LEFT, J_ALIGN_TOP);
|
jframe_set_align(l->frame, J_ALIGN_LEFT, J_ALIGN_TOP);
|
||||||
|
|
||||||
l->list = jlist_create(l->frame, info_function, paint_function);
|
l->list = jlist_create(info_function, paint_function, l->frame);
|
||||||
jwidget_set_stretch(l->list, 1, 1, false);
|
jwidget_set_stretch(l->list, 1, 1, false);
|
||||||
|
|
||||||
return l;
|
return l;
|
||||||
|
@ -67,8 +68,7 @@ static bool jscrolledlist_poly_event(void *l0, jevent e)
|
||||||
if(e.type == JLIST_MODEL_UPDATED && e.source == l->list)
|
if(e.type == JLIST_MODEL_UPDATED && e.source == l->list)
|
||||||
shake_scroll(l, true);
|
shake_scroll(l, true);
|
||||||
|
|
||||||
/* Allow the evnts to bubble up */
|
return jwidget_poly_event(l, e);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* jscrolledlist type definition */
|
/* jscrolledlist type definition */
|
||||||
|
@ -78,8 +78,8 @@ static jwidget_poly type_jscrolledlist = {
|
||||||
.event = jscrolledlist_poly_event,
|
.event = jscrolledlist_poly_event,
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((constructor(1002)))
|
__attribute__((constructor))
|
||||||
static void j_register_jscrolledlist(void)
|
static void j_register_jscrolledlist(void)
|
||||||
{
|
{
|
||||||
jscrolledlist_type_id = j_register_widget(&type_jscrolledlist, "jwidget");
|
jscrolledlist_type_id = j_register_widget(&type_jscrolledlist);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#define WIDGET_TYPES_MAX 32
|
#define WIDGET_TYPES_MAX 32
|
||||||
|
|
||||||
/* Polymorphic functions for jwidget */
|
/* Polymorphic functions for jwidget */
|
||||||
static jwidget_poly_render_t jwidget_poly_render;
|
|
||||||
static jwidget_poly_csize_t jwidget_poly_csize;
|
static jwidget_poly_csize_t jwidget_poly_csize;
|
||||||
|
|
||||||
/* jwidget type definition */
|
/* jwidget type definition */
|
||||||
|
@ -21,7 +20,7 @@ static jwidget_poly type_jwidget = {
|
||||||
.csize = jwidget_poly_csize,
|
.csize = jwidget_poly_csize,
|
||||||
.layout = NULL,
|
.layout = NULL,
|
||||||
.render = jwidget_poly_render,
|
.render = jwidget_poly_render,
|
||||||
.event = NULL,
|
.event = jwidget_poly_event,
|
||||||
.destroy = NULL,
|
.destroy = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ uint16_t JWIDGET_FOCUS_OUT;
|
||||||
// Polymorphic functions for widgets
|
// Polymorphic functions for widgets
|
||||||
//---
|
//---
|
||||||
|
|
||||||
static void jwidget_poly_render(void *w0, int x, int y)
|
void jwidget_poly_render(void *w0, int x, int y)
|
||||||
{
|
{
|
||||||
J_CAST(w)
|
J_CAST(w)
|
||||||
jlayout_stack *l;
|
jlayout_stack *l;
|
||||||
|
@ -86,6 +85,14 @@ static void jwidget_poly_csize(void *w0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool jwidget_poly_event(void *w0, jevent e)
|
||||||
|
{
|
||||||
|
J_CAST(w)
|
||||||
|
(void)w;
|
||||||
|
(void)e;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//---
|
//---
|
||||||
// Initialization
|
// Initialization
|
||||||
//---
|
//---
|
||||||
|
@ -651,7 +658,13 @@ void jwidget_render(void *w0, int x, int y)
|
||||||
/* TODO: jwidget_render(): More border types */
|
/* TODO: jwidget_render(): More border types */
|
||||||
|
|
||||||
if(g->background_color != C_NONE) {
|
if(g->background_color != C_NONE) {
|
||||||
drect(x1 + b.left, y1 + b.top, x2-1, y2-1, g->background_color);
|
int bgx = x1 + b.left;
|
||||||
|
int bgy = y1 + b.top;
|
||||||
|
|
||||||
|
if(bgx == 0 && bgy == 0 && x2 == DWIDTH && y2 == DHEIGHT)
|
||||||
|
dclear(g->background_color);
|
||||||
|
else
|
||||||
|
drect(bgx, bgy, x2-1, y2-1, g->background_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call the polymorphic render function at the top-left content point */
|
/* Call the polymorphic render function at the top-left content point */
|
||||||
|
@ -715,25 +728,14 @@ char const *jwidget_type(void *w0)
|
||||||
return widget_types[w->type]->name;
|
return widget_types[w->type]->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
int j_register_widget(jwidget_poly *poly, char const *inherits)
|
int j_register_widget(jwidget_poly *poly)
|
||||||
{
|
{
|
||||||
/* Resolve inheritance */
|
/* Resolve default behaviors */
|
||||||
if(inherits) {
|
if(!poly->csize) poly->csize = jwidget_poly_csize;
|
||||||
jwidget_poly const *base = NULL;
|
if(!poly->layout) poly->layout = NULL;
|
||||||
|
if(!poly->render) poly->render = jwidget_poly_render;
|
||||||
for(int i = 0; i < WIDGET_TYPES_MAX && !base; i++) {
|
if(!poly->event) poly->event = jwidget_poly_event;
|
||||||
if(widget_types[i] && !strcmp(widget_types[i]->name, inherits))
|
if(!poly->destroy) poly->destroy = NULL;
|
||||||
base = widget_types[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!base) return -1;
|
|
||||||
|
|
||||||
if(!poly->csize) poly->csize = base->csize;
|
|
||||||
if(!poly->layout) poly->layout = base->layout;
|
|
||||||
if(!poly->render) poly->render = base->render;
|
|
||||||
if(!poly->event) poly->event = base->event;
|
|
||||||
if(!poly->destroy) poly->destroy = base->destroy;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i < WIDGET_TYPES_MAX; i++) {
|
for(int i = 0; i < WIDGET_TYPES_MAX; i++) {
|
||||||
if(widget_types[i] == NULL) {
|
if(widget_types[i] == NULL) {
|
||||||
|
|
Loading…
Reference in a new issue