JustUI/include/justui/jwidget-api.h
Lephenixnoir f28d7a9cb8
jfkeys: add event processing to jfkeys
This requires jfkeys to _get_ the events, which needs to be done
manually by its owner to some degree (in gintctl, gscreen handles it).

Currently there are just 6 values 0..5 for F1..F6, but future expansion
for rotating menus with more than 6 entries and nested menus might add
to this set.
2024-09-06 16:19:01 +02:00

238 lines
9.7 KiB
C

//---
// JustUI.jwidget-API: API for subclassed widget types
//---
#ifndef _J_JWIDGET_API
#define _J_JWIDGET_API
#include <justui/defs.h>
#include <justui/p/preproc.h>
#include <justui/jevent.h>
#include <gint/keyboard.h>
//---
// Polymorphic operations on widgets
//
// The following operations are widget-type-dependent. Derived widget types
// must provide their implementation through a jwidget_poly structure. The
// supplied functions will then be called at different points during the
// lifetime of the derived widgets.
//---
/* jwidget_poly_csize_t: Determine the natural content size of a widget
This function is called during layout. It should set the natural size of the
content box of the widget in (w->w) and (w->h). If the widget has a layout,
this function will not be called; instead, the layout will determine the
natural content size. Thus, this function is mostly useful for content
widgets and not useful for containers.
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
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);
/* jwidget_poly_layout_t: Layout a widget after its size has been set
This function is called during the second phase of the layout process, if
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
children. If the widget has a layout, the layout's specialized function is
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);
/* jwidget_poly_render_t: Render a widget
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
render widget-specific visuals. If the widget is clipped (as specified by
`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 render its children. In the simple case where all
children can be rendered at the same time, you can simply call
`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).
It will draw the geometry and call the polymorphic renderer of the child at
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);
extern jwidget_poly_render_t jwidget_poly_render;
/* jwidget_poly_event_t: Handle an event
This function is called when an event is targeted at a widget, including for
key events. This function is somewhat of a catch-all function for dynamic
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
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);
extern jwidget_poly_event_t jwidget_poly_event;
/* jwidget_poly_destroy_t: Destroy a widget's specific resources
This function must destroy the widget-specific resources. It is called by
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
widget-specific resources to free. It should not call the "base" version of
the function. */
typedef void jwidget_poly_destroy_t(void *w);
/* jwidget_poly: Polymorphic interface for a widget type */
typedef struct {
/* Type name, used for display and inheritance */
char const *name;
/* 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_layout_t *layout;
jwidget_poly_render_t *render;
jwidget_poly_event_t *event;
jwidget_poly_destroy_t *destroy;
} jwidget_poly;
//---
// Widget registration
//---
/* j_register_widget(): Register a new widget type
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
polymorphic structure must outlive all widgets of the custom type. */
int j_register_widget(jwidget_poly *poly);
/* j_register_event(): Register a new event type
This function returns a new ID to set in the (type) field of jevent objects.
The ID is unique, and can be trusted to be always valid (unless you register
more than 64k events in which case you asked for the trouble). */
int j_register_event(void);
//---
// Helpers to implement custom widget types
//---
/* jwidget_init(): Initialize a widget
This function should be called in the constructor for the subclassed widget
type, preferably as soon as possible. It initializes common widget
attributes, sets the widget type, and declares the parent.
A subclassed widget type must have a jwidget as a first member (so that the
address of any instance of the subclassed widget is a valid pointer to
jwidget), and that jwidget must be initialized with jwidget_init().
@w Widget to initialize
@type Type ID, as returned by j_register_widget()
@parent Parent, same as in jwidget_create() */
void jwidget_init(jwidget *w, int type, void *parent);
/* jwidget_msize(): Compute and apply the natural size of a widget's margin-box
This function computes the widget's natural margin-box size. It determines
the natural content size with the csize() function of either the layout or
the widget type, then adds the geometry.
The margin-box size is stored in the (w) and (h) attributes of the widget.
This function should only be called during the first phase of the layout,
to implement subclassed csize() functions. Usually, the parent will
implement a customn csize() function by combining the position and msize()
of its children. */
void jwidget_msize(void *w);
/* jwidget_emit(): Emit an upwards event from this widget
This function walks up the tree until it finds a jscene that can store the
event. If there is no jscene in the tree, the event is ignored. The (source)
field can be omitted and will be set to the widget's address by default. */
void jwidget_emit(void *w, jevent e);
/* jwidget_event(): Send a downwards event to a widget
This function calls the polymorphic event function of the targeted widget to
notify it of the specified event. */
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 */