diff --git a/include/justui/jwidget.h b/include/justui/jwidget.h index 3cdc7ef..860a065 100644 --- a/include/justui/jwidget.h +++ b/include/justui/jwidget.h @@ -89,8 +89,10 @@ typedef struct jwidget { uint visible :1; /* Widget is floating outside the layout (and positioned manually) */ uint floating :1; + /* Widget is clipped during rendering */ + uint clipped :1; - uint :23; + uint :22; } jwidget; @@ -358,6 +360,23 @@ bool jwidget_visible(void *w); /* jwidget_set_visible(): Hide or show a widget */ void jwidget_set_visible(void *w, bool visible); +/* jwidget_clipped(): Whether widget is clipped + + If a widget is clipped then its rendering function cannot draw pixels + outside of its bounding box. There is no performance cost to this feature + because it relies on underlying gint rendering functions already supporting + clipping. + + This is disabled by default because it is convenient to have widgets draw + outside their bounding box. For instance it is easier to align a single- + line label by setting the font's bearing as its height, and then drawing + glyphs' tails outside the bouding box. It is also harder to spot layout + issues if the widgets are clipped away. */ +bool jwidget_clipped(void *w); + +/* jwidget_set_clipped(): Set a widget's rendering clipping preference */ +void jwidget_set_clipped(void *w, bool clipped); + /* jwidget_needs_update(): Check whether the tree needs to be re-rendered If this function returns true, you should re-render the tree. Aditionally, diff --git a/src/jwidget.c b/src/jwidget.c index 75d1115..53fd4b5 100644 --- a/src/jwidget.c +++ b/src/jwidget.c @@ -109,6 +109,7 @@ void jwidget_init(jwidget *w, int type, void *parent) w->dirty = 1; w->visible = 1; w->floating = 0; + w->clipped = 0; w->type = type; w->geometry = NULL; @@ -597,6 +598,21 @@ void jwidget_set_floating(void *w0, bool floating) if(w->parent) w->parent->dirty = 1; } +bool jwidget_clipped(void *w0) +{ + J_CAST(w) + return w->clipped; +} + +void jwidget_set_clipped(void *w0, bool clipped) +{ + J_CAST(w) + if(w->clipped == clipped) return; + + w->clipped = (clipped != 0); + w->update = 1; +} + //--- // Rendering //--- @@ -643,7 +659,18 @@ void jwidget_render(void *w0, int x, int y) y += g->margin.top + b.top + g->padding.top; jwidget_poly const *poly = widget_types[w->type]; - if(poly->render) poly->render(w, x, y); + if(poly->render) { + if(w->clipped) { + struct dwindow win = { x, y, x+cw, y+ch }; + win = intersect_dwindow(win, dwindow); + struct dwindow old_window = dwindow_set(win); + poly->render(w, x, y); + dwindow_set(old_window); + } + else { + poly->render(w, x, y); + } + } w->update = 0; } diff --git a/src/util.h b/src/util.h index 00d9888..7ebe7a8 100644 --- a/src/util.h +++ b/src/util.h @@ -6,6 +6,7 @@ #define _J_UTIL #include +#include /* Clamp a value between two ends. */ __attribute__((always_inline)) @@ -20,4 +21,16 @@ static inline int clamp(int value, int min, int max) /* Code point for a character input */ uint32_t keymap_translate(int key, bool shift, bool alpha); +/* Intersect two dwindow settings. */ +static inline struct dwindow intersect_dwindow( + struct dwindow d1, struct dwindow d2) +{ + struct dwindow win; + win.left = max(d1.left, d2.left); + win.top = max(d1.top, d2.top); + win.right = max(min(d1.right, d2.right), win.left); + win.bottom = max(min(d1.bottom, d2.bottom), win.top); + return win; +} + #endif /* _J_UTIL */