jscene: basic touch support with "inspector"-like debug

This commit is contained in:
Lephenixnoir 2025-04-13 16:44:01 +02:00
parent 0878c16182
commit cd73b2ea6f
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
5 changed files with 177 additions and 3 deletions

View file

@ -5,6 +5,17 @@
#ifndef _JUSTUI_CONFIG
#define _JUSTUI_CONFIG
#include <gint/config.h>
#define J_VERSION "@JustUI_VERSION@"
#if GINT_HW_CP
#define J_CONFIG_TOUCH 1
#else
#define J_CONFIG_TOUCH 0
#endif
/* Set to 1 to enable a touch-following widget inspector. */
#define J_CONFIG_TOUCH_INSPECTOR (J_CONFIG_TOUCH && 1)
#endif /* _JUSTUI_CONFIG */

View file

@ -8,6 +8,7 @@
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <justui/jevent.h>
#include <justui/config.h>
#define JSCENE_QUEUE_SIZE 32
@ -36,6 +37,9 @@ typedef struct {
/* Whether jscene_run() will autopaint */
bool autopaint;
/* Last coordinates a touch cursor was seen at */
int16_t touch_last_x, touch_last_y;
} jscene;
/* Events */
@ -65,6 +69,21 @@ jscene *jscene_owning(void *widget);
(x,y) point. */
void jscene_render(jscene *scene);
/* jscene_widget_at(): Find top-most widget at specified location
Returns the top-most widget at the given scene-local location.
* If there is no widget in the scene covering this location, returns NULL.
* If there is exactly one, returns it.
* If there are multiple, this function returns the first found by depth-
first search where children are enumerated floating first, non-floating
next, and increasing within child index order within each group.
* For widgets with a stacked layout, only the active child is considered.
If content_only is set, this function only intersects with the content box.
Otherwise, an intersection with the full box (padding, border and margin
included) is performed. */
jwidget *jscene_widget_at(jscene *scene, int x, int y, bool content_only);
//---
// Events sent from the scene to the user
//---

View file

@ -339,6 +339,18 @@ bool jwidget_layout_dirty(void *scene_root);
need to keep the layout up-to-date before doing it. */
void jwidget_layout(void *scene_root);
/* jwidget_absolute_x(): Absolute x-position of a widget
jwidget_absolute_y(): Absolute y-position of a widget
jwidget_absolute_content_x(): Absolute x-position of a widget's content box
jwidget_absolute_content_y(): Absolute y-position of a widget's content box
These are actually based on the root's coordinates, which are typically 0,0
with a full-screen scene. */
int jwidget_absolute_x(void *w);
int jwidget_absolute_y(void *w);
int jwidget_absolute_content_x(void *w);
int jwidget_absolute_content_y(void *w);
/* jwidget_width(): With of a widget's content box
jwidget_height(): Height of a widget's content box

View file

@ -62,6 +62,9 @@ jscene *jscene_create(int x, int y, int w, int h, void *parent)
s->poweroff = true;
s->autopaint = false;
s->touch_last_x = -1;
s->touch_last_y = -1;
/* Prepare first layout/paint operation */
s->widget.dirty = 1;
@ -84,10 +87,94 @@ jscene *jscene_owning(void *w0)
return NULL;
}
void jscene_render(jscene *scene)
void jscene_render(jscene *s)
{
jwidget_layout(scene);
jwidget_render(scene, scene->x, scene->y);
jwidget_layout(s);
jwidget_render(s, s->x, s->y);
#if J_CONFIG_TOUCH_INSPECTOR
if(s->touch_last_x >= 0 && s->touch_last_y >= 0) {
jwidget *w =
jscene_widget_at(s, s->touch_last_x, s->touch_last_y, true);
if(w) {
int x1 = jwidget_absolute_content_x(w);
int y1 = jwidget_absolute_content_y(w);
int x2 = x1 + jwidget_content_width(w) - 1;
int y2 = y1 + jwidget_content_height(w) - 1;
drect_border(x1, y1, x2, y2, C_NONE, 1, C_RED);
dprint_opt(DWIDTH-1, 0, C_WHITE, C_RED, DTEXT_RIGHT, DTEXT_TOP,
"%s", jwidget_type(w));
}
}
#endif
}
/* Find sub-widget based on w-local coordinates. */
static jwidget *jscene_widget_at_rec(
jwidget *w, int x, int y, bool content_only, bool visible_only)
{
if(!w)
return NULL;
jwidget *found;
jwidget_geometry const *g = jwidget_geometry_r(w);
int xC = g->margin.left + g->border.left + g->padding.left;
int yC = w->y + g->margin.top + g->border.top + g->padding.top;
uint wC = jwidget_content_width(w);
uint hC = jwidget_content_height(w);
uint wF = jwidget_full_width(w);
uint hF = jwidget_full_height(w);
/* Check if we intersect w at all */
bool intersects;
if(content_only)
intersects = ((uint)(x - xC) < wC) && ((uint)(y - yC) < hC);
else
intersects = (uint)x < wF && (uint)y < hF;
if(!intersects)
return NULL;
/* If we have a stacked widget, only consider the visible child */
jlayout_stack *stack;
if((stack = jlayout_get_stack(w))) {
if(stack->active < 0)
return w;
jwidget *child = w->children[stack->active];
found = jscene_widget_at_rec(child, x - xC, y - yC, content_only,
visible_only);
return found ? found : w;
}
/* Try to find a descendant of a floating child that intersects... */
for(int k = 0; k < w->child_count; k++) {
jwidget *child = w->children[k];
if(child->visible >= visible_only && child->floating) {
found = jscene_widget_at_rec(child, x - xC, y - yC, content_only,
visible_only);
if(found)
return found;
}
}
/* ... or a descendant of a non-floating child */
for(int k = 0; k < w->child_count; k++) {
jwidget *child = w->children[k];
if(child->visible >= visible_only && !child->floating) {
found = jscene_widget_at_rec(child, x - xC, y - yC, content_only,
visible_only);
if(found)
return found;
}
}
/* If no descendants do, then the best match is w itself. */
return w;
}
jwidget *jscene_widget_at(jscene *scene, int x, int y, bool content_only)
{
return jscene_widget_at_rec(&scene->widget, x, y, content_only, true);
}
//---
@ -260,6 +347,19 @@ static jevent poll_next_unhandled_event(keydev_t *d, jscene *s)
/* Then try to dequeue keyboard events, if there are any. */
while((k = keydev_read(d, false, NULL)).type != KEYEV_NONE) {
/* Keep track of where the touch cursor was last seen. */
#if J_CONFIG_TOUCH_INSPECTOR
if(k.type == KEYEV_TOUCH_DOWN || k.type == KEYEV_TOUCH_DRAG) {
s->touch_last_x = k.x - s->x;
s->touch_last_y = k.y - s->y;
s->widget.update = true;
}
if(k.type == KEYEV_TOUCH_UP) {
s->touch_last_x = -1;
s->touch_last_y = -1;
s->widget.update = true;
}
#endif
/* Auto return-to-menu */
if(k.type == KEYEV_DOWN && k.key == KEY_MENU && !k.shift && !k.alpha) {
if(s->mainmenu) {

View file

@ -567,6 +567,38 @@ void jwidget_layout(void *root0)
jwidget_layout_apply(root);
}
int jwidget_absolute_x(void *w0)
{
J_CAST(w)
return w ? w->x + jwidget_absolute_x(w->parent) : 0;
}
int jwidget_absolute_y(void *w0)
{
J_CAST(w)
return w ? w->y + jwidget_absolute_y(w->parent) : 0;
}
int jwidget_absolute_content_x(void *w0)
{
J_CAST(w)
if(!w)
return 0;
jwidget_geometry const *g = jwidget_geometry_r(w);
return jwidget_absolute_content_x(w->parent) +
w->x + g->margin.left + g->border.left + g->padding.left;
}
int jwidget_absolute_content_y(void *w0)
{
J_CAST(w)
if(!w)
return 0;
jwidget_geometry const *g = jwidget_geometry_r(w);
return jwidget_absolute_content_y(w->parent) +
w->y + g->margin.top + g->border.top + g->padding.top;
}
int jwidget_content_width(void *w0)
{
J_CAST(w)