mirror of
https://git.planet-casio.com/Lephenixnoir/JustUI.git
synced 2024-12-28 04:23:40 +01:00
Compare commits
3 commits
7a5101360a
...
4728c6ecbe
Author | SHA1 | Date | |
---|---|---|---|
|
4728c6ecbe | ||
|
33e9962209 | ||
|
57a460894f |
10 changed files with 267 additions and 67 deletions
|
@ -46,8 +46,9 @@ New keyboard focus system.
|
|||
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.
|
||||
has is NULL, this call just removes the surrounding scope's target.
|
||||
TODO: If the widget is FOCUS_REJECT, clear scopes from that widget up
|
||||
until reaching the scene.
|
||||
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.
|
||||
|
@ -73,19 +74,20 @@ New keyboard focus system.
|
|||
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.
|
||||
TODO: Implement stack layout logic
|
||||
|
||||
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:
|
||||
- Update focus flags when removing from parent
|
||||
- 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.
|
||||
|
|
|
@ -67,7 +67,7 @@ typedef struct {
|
|||
jalign block_valign;
|
||||
/* Text alignment */
|
||||
jalign text_align;
|
||||
/* Pixels of spacing between each line, in addition to font->height */
|
||||
/* Pixels of spacing between lines, in addition to font->line_distance */
|
||||
int8_t line_spacing;
|
||||
|
||||
/* Text to display */
|
||||
|
@ -76,12 +76,16 @@ typedef struct {
|
|||
of line number n */
|
||||
DECLARE_VEC(uint16_t, breaks);
|
||||
|
||||
/* Block width (maximum length of a rendered line) */
|
||||
uint16_t block_width;
|
||||
/* Text wrapping mode */
|
||||
enum jwrapmode wrap_mode;
|
||||
/* Whether the text has been allocated by the label or supplied by user */
|
||||
int8_t owns_text;
|
||||
/* Block width (maximum length of a rendered line) */
|
||||
uint16_t block_width;
|
||||
bool owns_text :1;
|
||||
/* Whether to preserve spaces around line-wrapping newlines */
|
||||
bool wrapped_newline_spacing :1;
|
||||
|
||||
uint :5;
|
||||
|
||||
/* Color and font of text; if NULL, gint's default font is used */
|
||||
int color;
|
||||
|
@ -130,5 +134,6 @@ void jlabel_set_line_spacing(jlabel *l, int line_spacing);
|
|||
void jlabel_set_wrap_mode(jlabel *l, jwrapmode mode);
|
||||
void jlabel_set_text_color(jlabel *l, int color);
|
||||
void jlabel_set_font(jlabel *l, font_t const *font);
|
||||
void jlabel_set_wrapped_newline_spacing(jlabel *l, bool preserve);
|
||||
|
||||
#endif /* _J_JLABEL */
|
||||
|
|
|
@ -21,8 +21,6 @@ typedef struct {
|
|||
|
||||
/* Location on screen */
|
||||
int16_t x, y;
|
||||
/* Widget with focus */
|
||||
jwidget *focus;
|
||||
|
||||
/* Circular event queue */
|
||||
jevent queue[JSCENE_QUEUE_SIZE];
|
||||
|
|
|
@ -67,6 +67,9 @@ typedef struct jwidget {
|
|||
jlayout_grid layout_grid;
|
||||
};
|
||||
|
||||
/* Focused subwidget for focus scopes */
|
||||
struct jwidget *focus_scope_target;
|
||||
|
||||
/* Widget type, used to find polymorphic operations */
|
||||
uint8_t type;
|
||||
/* Number of children */
|
||||
|
@ -91,8 +94,13 @@ typedef struct jwidget {
|
|||
uint floating :1;
|
||||
/* Widget is clipped during rendering */
|
||||
uint clipped :1;
|
||||
/* Focus policy */
|
||||
uint focus_policy :2;
|
||||
/* Focus flags */
|
||||
uint focused :1;
|
||||
uint active_focused :1;
|
||||
|
||||
uint :22;
|
||||
uint :18;
|
||||
|
||||
} jwidget;
|
||||
|
||||
|
@ -106,6 +114,17 @@ typedef enum {
|
|||
|
||||
} jwidget_border_style;
|
||||
|
||||
/* jwidget_focus_policy: Options for receiving and handling keyboard focus */
|
||||
typedef enum {
|
||||
/* The widget does not accept focus or interact with keyboard (default) */
|
||||
J_FOCUS_POLICY_REJECT,
|
||||
/* The widget accepts keyboard input and hides it from descendants */
|
||||
J_FOCUS_POLICY_ACCEPT,
|
||||
/* The widget accepts keyboard focus and forwards it to a descendant */
|
||||
J_FOCUS_POLICY_SCOPE,
|
||||
|
||||
} jwidget_focus_policy_t;
|
||||
|
||||
/* jwidget_geometry: Built-in positioning and border geometry
|
||||
|
||||
Every widget has a "geometry", which consists of a border and two layers of
|
||||
|
@ -143,14 +162,13 @@ typedef struct jwidget_geometry {
|
|||
|
||||
} jwidget_geometry;
|
||||
|
||||
/* Downwards key event: widget is notified of a key press that ocurred while it
|
||||
had active focus.
|
||||
/* Key press event; the widget has active focus and a key was pressed.
|
||||
-> .data.key: Key event */
|
||||
extern uint16_t JWIDGET_KEY;
|
||||
/* Downwards focus-in event: the widget has just received focus */
|
||||
extern uint16_t JWIDGET_FOCUS_IN;
|
||||
/* Downwards focus-out event: the widget has just lost focus */
|
||||
extern uint16_t JWIDGET_FOCUS_OUT;
|
||||
/* The widget's focus state (either .focused or .active_focused) changed. */
|
||||
extern uint16_t JWIDGET_FOCUS_CHANGED;
|
||||
/* The widget is a scope and its target changed. */
|
||||
extern uint16_t JWIDGET_FOCUS_TARGET_CHANGED;
|
||||
|
||||
//---
|
||||
// Creation and destruction
|
||||
|
@ -395,6 +413,53 @@ bool jwidget_needs_update(void *w);
|
|||
widgets if there has been changes. */
|
||||
void jwidget_render(void *w, int x, int y);
|
||||
|
||||
//---
|
||||
// Keyboard focus
|
||||
//---
|
||||
|
||||
/* Change the widget's focus policy. This is usually done in the constructor
|
||||
but can be done anytime. If the policy is changed to JWIDGET_FOCUS_REJECT
|
||||
while the widget has focus, the focus will be lost. */
|
||||
void jwidget_set_focus_policy(void *w, jwidget_focus_policy_t fp);
|
||||
|
||||
/* Check whether a widget is currently focused within its surrounding scope. */
|
||||
GINLINE static bool jwidget_has_focus(void *w)
|
||||
{
|
||||
return ((jwidget *)w)->focused;
|
||||
}
|
||||
/* Check whether a widget is current in the key event propagation chain. */
|
||||
GINLINE static bool jwidget_has_active_focus(void *w)
|
||||
{
|
||||
return ((jwidget *)w)->active_focused;
|
||||
}
|
||||
|
||||
/* Change the target of a focus scope widget. */
|
||||
void jwidget_scope_set_target(void *fs, void *target);
|
||||
|
||||
/* Get the target of a focus scope, NULL if none or not a scope. */
|
||||
GINLINE static jwidget *jwidget_scope_get_target(void *fs)
|
||||
{
|
||||
return ((jwidget *)fs)->focus_scope_target;
|
||||
}
|
||||
|
||||
/* Clear the target of a focus scope widget. */
|
||||
GINLINE static void jwidget_scope_clear_focus(void *fs)
|
||||
{
|
||||
return jwidget_scope_set_target(fs, NULL);
|
||||
}
|
||||
|
||||
/* Context function that returns the immediately surrounding scope that owns
|
||||
this widget, NULL if there's none. Since jscene is a scope, in general there
|
||||
should always be one. */
|
||||
jwidget *jwidgetctx_enclosing_focus_scope(void *w);
|
||||
|
||||
/* Context function that gives w focus within its enclosing focus scope. */
|
||||
void jwidgetctx_grab_focus(void *w);
|
||||
|
||||
/* Context function that drops the focus from w (if it has it) within its
|
||||
enclosing focus scope. */
|
||||
void jwidgetctx_drop_focus(void *w);
|
||||
|
||||
//---
|
||||
// Misc
|
||||
//---
|
||||
|
|
|
@ -52,6 +52,7 @@ jfileselect *jfileselect_create(void *parent)
|
|||
if(!fs) return NULL;
|
||||
|
||||
jwidget_init(&fs->widget, jfileselect_type_id, parent);
|
||||
jwidget_set_focus_policy(fs, J_FOCUS_POLICY_SCOPE);
|
||||
|
||||
jinput *input = jinput_create("Filename: ", 32, fs);
|
||||
if(!input) {
|
||||
|
@ -116,14 +117,14 @@ static void start_input(jfileselect *fs)
|
|||
{
|
||||
fs->input_mode = true;
|
||||
jinput_clear(fs->saveas_input);
|
||||
jwidget_event(fs->saveas_input, (jevent){ .type = JWIDGET_FOCUS_IN });
|
||||
jwidget_scope_set_target(fs, fs->saveas_input);
|
||||
fs->widget.update = true;
|
||||
}
|
||||
|
||||
static void stop_input(jfileselect *fs)
|
||||
{
|
||||
fs->input_mode = false;
|
||||
jwidget_event(fs->saveas_input, (jevent){ .type = JWIDGET_FOCUS_OUT });
|
||||
jwidget_scope_set_target(fs, NULL);
|
||||
fs->widget.update = true;
|
||||
}
|
||||
|
||||
|
|
11
src/jinput.c
11
src/jinput.c
|
@ -45,6 +45,7 @@ jinput *jinput_create(char const *prompt, size_t length, void *parent)
|
|||
if(!i) return NULL;
|
||||
|
||||
jwidget_init(&i->widget, jinput_type_id, parent);
|
||||
jwidget_set_focus_policy(i, J_FOCUS_POLICY_ACCEPT);
|
||||
|
||||
i->color = C_BLACK;
|
||||
jinput_set_font(i, NULL);
|
||||
|
@ -242,14 +243,8 @@ static bool jinput_poly_event(void *i0, jevent e)
|
|||
{
|
||||
jinput *i = i0;
|
||||
|
||||
if(e.type == JWIDGET_FOCUS_IN) {
|
||||
i->cursor = i->size - 1;
|
||||
i->mode = 0;
|
||||
i->widget.update = 1;
|
||||
}
|
||||
|
||||
if(e.type == JWIDGET_FOCUS_OUT) {
|
||||
i->cursor = -1;
|
||||
if(e.type == JWIDGET_FOCUS_CHANGED) {
|
||||
i->cursor = jwidget_has_focus(i) ? (i->size - 1) : -1;
|
||||
i->mode = 0;
|
||||
i->widget.update = 1;
|
||||
}
|
||||
|
|
32
src/jlabel.c
32
src/jlabel.c
|
@ -21,16 +21,17 @@ jlabel *jlabel_create(char const *text, void *parent)
|
|||
jwidget_init(&l->widget, jlabel_type_id, parent);
|
||||
|
||||
l->block_halign = J_ALIGN_LEFT;
|
||||
l->block_valign = J_ALIGN_MIDDLE;
|
||||
l->block_valign = J_ALIGN_TOP;
|
||||
l->text_align = J_ALIGN_LEFT;
|
||||
|
||||
l->line_spacing = 1;
|
||||
l->line_spacing = 0;
|
||||
l->color = C_BLACK;
|
||||
l->font = NULL;
|
||||
|
||||
l->wrap_mode = J_WRAP_NONE;
|
||||
l->text = text;
|
||||
l->owns_text = false;
|
||||
l->wrapped_newline_spacing = false;
|
||||
|
||||
vec_init(&l->breaks_vec, sizeof *l->breaks);
|
||||
|
||||
|
@ -188,6 +189,12 @@ void jlabel_set_font(jlabel *l, font_t const *font)
|
|||
l->widget.dirty = 1;
|
||||
}
|
||||
|
||||
void jlabel_set_wrapped_newline_spacing(jlabel *l, bool preserve)
|
||||
{
|
||||
l->wrapped_newline_spacing = preserve;
|
||||
l->widget.dirty = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Polymorphic widget operations
|
||||
//---
|
||||
|
@ -236,7 +243,7 @@ static void jlabel_poly_layout(void *l0)
|
|||
/* Start of line */
|
||||
add_break(l, str - l->text);
|
||||
|
||||
/* A "\n" forces a newline in all wrap omdes */
|
||||
/* A "\n" forces a newline in all wrap modes */
|
||||
char const *end_of_line = strchrnul(str, '\n');
|
||||
|
||||
/* Also consider word or letters boundaries */
|
||||
|
@ -257,14 +264,17 @@ static void jlabel_poly_layout(void *l0)
|
|||
}
|
||||
}
|
||||
|
||||
bool natural_break = (*end_of_line == '\n');
|
||||
char const *next_start = end_of_line + (*end_of_line == '\n');
|
||||
|
||||
/* Skip trailing spaces on this line */
|
||||
while(end_of_line > str && end_of_line[-1] == ' ')
|
||||
end_of_line--;
|
||||
/* Skip leading spaces on the next line */
|
||||
while(next_start[0] == ' ')
|
||||
next_start++;
|
||||
if(!natural_break && !l->wrapped_newline_spacing) {
|
||||
/* Skip trailing spaces on this line */
|
||||
while(end_of_line > str && end_of_line[-1] == ' ')
|
||||
end_of_line--;
|
||||
/* Skip leading spaces on the next line */
|
||||
while(next_start[0] == ' ')
|
||||
next_start++;
|
||||
}
|
||||
|
||||
add_break(l, end_of_line - l->text);
|
||||
|
||||
|
@ -295,7 +305,7 @@ static void jlabel_poly_render(void *l0, int x, int y)
|
|||
/* Position the block vertically */
|
||||
|
||||
int lines = l->breaks_vec.size / 2;
|
||||
int block_height = lines * (f->line_height + l->line_spacing) -
|
||||
int block_height = lines * (f->line_distance + l->line_spacing) -
|
||||
l->line_spacing;
|
||||
|
||||
if(l->block_valign == J_ALIGN_MIDDLE)
|
||||
|
@ -331,7 +341,7 @@ static void jlabel_poly_render(void *l0, int x, int y)
|
|||
dtext_opt(x + dx, y, l->color, C_NONE, DTEXT_LEFT, DTEXT_TOP,
|
||||
str, line_length);
|
||||
|
||||
y += f->line_height + l->line_spacing;
|
||||
y += f->line_distance + l->line_spacing;
|
||||
str = l->text + l->breaks[i];
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ jlist *jlist_create(jlist_item_info_function info_function,
|
|||
return NULL;
|
||||
|
||||
jwidget_init(&l->widget, jlist_type_id, parent);
|
||||
jwidget_set_focus_policy(l, J_FOCUS_POLICY_ACCEPT);
|
||||
|
||||
l->item_count = 0;
|
||||
l->items = NULL;
|
||||
|
|
45
src/jscene.c
45
src/jscene.c
|
@ -44,13 +44,17 @@ jscene *jscene_create(int x, int y, int w, int h, void *parent)
|
|||
if(!s) return NULL;
|
||||
|
||||
jwidget_init(&s->widget, jscene_type_id, parent);
|
||||
jwidget_set_focus_policy(s, J_FOCUS_POLICY_SCOPE);
|
||||
jwidget_set_fixed_size(s, w, h);
|
||||
jlayout_set_vbox(s);
|
||||
|
||||
/* The scene is where active focus originates */
|
||||
s->widget.focused = 1;
|
||||
s->widget.active_focused = 1;
|
||||
|
||||
s->x = x;
|
||||
s->y = y;
|
||||
|
||||
s->focus = NULL;
|
||||
s->queue_first = 0;
|
||||
s->queue_next = 0;
|
||||
s->lost_events = 0;
|
||||
|
@ -128,27 +132,37 @@ void jscene_queue_event(jscene *s, jevent e)
|
|||
|
||||
void *jscene_focused_widget(jscene *s)
|
||||
{
|
||||
return s->focus;
|
||||
jwidget *w = &s->widget;
|
||||
while(w->focus_scope_target)
|
||||
w = w->focus_scope_target;
|
||||
return w;
|
||||
}
|
||||
|
||||
void jscene_set_focused_widget(jscene *s, void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
|
||||
/* Unfocus only at the top level */
|
||||
if(!w) {
|
||||
jwidget_scope_set_target(s, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if(w->focus_policy == J_FOCUS_POLICY_REJECT)
|
||||
return;
|
||||
|
||||
/* Check that (s) is an ancestor of (w) */
|
||||
if(w) for(jwidget *anc = w; anc != (jwidget *)s; anc = anc->parent) {
|
||||
for(jwidget *anc = w; anc != (jwidget *)s; anc = anc->parent) {
|
||||
if(anc == NULL) return;
|
||||
}
|
||||
|
||||
/* Focus out old focused widget */
|
||||
if(s->focus) jwidget_event(s->focus,
|
||||
(jevent){ .type = JWIDGET_FOCUS_OUT, .source = s->focus });
|
||||
|
||||
s->focus = w;
|
||||
|
||||
/* Focus in newly-selected widget */
|
||||
if(w) jwidget_event(w,
|
||||
(jevent){ .type = JWIDGET_FOCUS_IN, .source = w });
|
||||
/* Set targets in every scope along the way up */
|
||||
jwidget *scope = w;
|
||||
while(scope != NULL) {
|
||||
scope = jwidgetctx_enclosing_focus_scope(scope);
|
||||
jwidget_scope_set_target(scope, w);
|
||||
w = scope;
|
||||
}
|
||||
}
|
||||
|
||||
void jscene_show_and_focus(jscene *scene, void *w0)
|
||||
|
@ -182,12 +196,13 @@ void jscene_show_and_focus(jscene *scene, void *w0)
|
|||
|
||||
bool jscene_process_key_event(jscene *scene, key_event_t event)
|
||||
{
|
||||
jwidget *candidate = scene->focus;
|
||||
jwidget *candidate = jscene_focused_widget(scene);
|
||||
jevent e = { .type = JWIDGET_KEY, .key = event };
|
||||
|
||||
while(candidate) {
|
||||
if(jwidget_event(candidate, e)) return true;
|
||||
candidate = candidate->parent;
|
||||
if(jwidget_event(candidate, e))
|
||||
return true;
|
||||
candidate = jwidgetctx_enclosing_focus_scope(candidate);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
138
src/jwidget.c
138
src/jwidget.c
|
@ -31,9 +31,10 @@ static jwidget_poly *widget_types[WIDGET_TYPES_MAX] = {
|
|||
};
|
||||
|
||||
/* Events */
|
||||
uint16_t JWIDGET_KEY;
|
||||
uint16_t JWIDGET_FOCUS_IN;
|
||||
uint16_t JWIDGET_FOCUS_OUT;
|
||||
J_DEFINE_EVENTS(
|
||||
JWIDGET_KEY,
|
||||
JWIDGET_FOCUS_CHANGED,
|
||||
JWIDGET_FOCUS_TARGET_CHANGED);
|
||||
|
||||
//---
|
||||
// Polymorphic functions for widgets
|
||||
|
@ -117,9 +118,13 @@ void jwidget_init(jwidget *w, int type, void *parent)
|
|||
w->visible = 1;
|
||||
w->floating = 0;
|
||||
w->clipped = 0;
|
||||
w->focus_policy = J_FOCUS_POLICY_REJECT;
|
||||
w->focused = 0;
|
||||
w->active_focused = 0;
|
||||
|
||||
w->type = type;
|
||||
w->geometry = NULL;
|
||||
w->focus_scope_target = NULL;
|
||||
|
||||
w->x = 0;
|
||||
w->y = 0;
|
||||
|
@ -605,6 +610,10 @@ void jwidget_set_floating(void *w0, bool floating)
|
|||
if(w->parent) w->parent->dirty = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Rendering
|
||||
//---
|
||||
|
||||
bool jwidget_clipped(void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
|
@ -620,10 +629,6 @@ void jwidget_set_clipped(void *w0, bool clipped)
|
|||
w->update = 1;
|
||||
}
|
||||
|
||||
//---
|
||||
// Rendering
|
||||
//---
|
||||
|
||||
void jwidget_render(void *w0, int x, int y)
|
||||
{
|
||||
J_CAST(w)
|
||||
|
@ -692,6 +697,117 @@ void jwidget_render(void *w0, int x, int y)
|
|||
w->update = 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard focus
|
||||
//---
|
||||
|
||||
void jwidget_set_focus_policy(void *w0, jwidget_focus_policy_t fp)
|
||||
{
|
||||
J_CAST(w)
|
||||
if(w->focus_policy == fp)
|
||||
return;
|
||||
|
||||
/* If this was a scope, clear it (otherwise the surrounding scope, which is
|
||||
going to expand, could pick up a second, untargeted focused widget). */
|
||||
if(w->focus_policy == J_FOCUS_POLICY_SCOPE)
|
||||
jwidget_scope_clear_focus(w);
|
||||
|
||||
/* Remove focus if we're no longer accepting it */
|
||||
if(fp == J_FOCUS_POLICY_REJECT && jwidget_has_focus(w))
|
||||
jwidgetctx_drop_focus(w);
|
||||
|
||||
w->focus_policy = fp;
|
||||
}
|
||||
|
||||
static void notify_focus_changed(jwidget *w)
|
||||
{
|
||||
jevent e;
|
||||
e.source = w;
|
||||
e.type = JWIDGET_FOCUS_CHANGED;
|
||||
jwidget_event(w, e);
|
||||
}
|
||||
|
||||
static void set_focus_chain_active(
|
||||
jwidget *w, bool set, bool notify_toplevel, bool bottom_up)
|
||||
{
|
||||
bool recursive =
|
||||
(w->focus_policy == J_FOCUS_POLICY_SCOPE && w->focus_scope_target);
|
||||
|
||||
if(recursive && bottom_up)
|
||||
set_focus_chain_active(w->focus_scope_target, set, true, bottom_up);
|
||||
|
||||
w->active_focused = set;
|
||||
if(notify_toplevel)
|
||||
notify_focus_changed(w);
|
||||
|
||||
if(recursive && !bottom_up)
|
||||
set_focus_chain_active(w->focus_scope_target, set, true, bottom_up);
|
||||
}
|
||||
|
||||
void jwidget_scope_set_target(void *fs0, void *target0)
|
||||
{
|
||||
J_CAST(fs, target)
|
||||
if(!fs || fs->focus_policy != J_FOCUS_POLICY_SCOPE
|
||||
|| fs->focus_scope_target == target)
|
||||
return;
|
||||
|
||||
jwidget *oldt = fs->focus_scope_target;
|
||||
|
||||
if(oldt) {
|
||||
/* First, if we have active focus, remove it from the entire chain */
|
||||
if(jwidget_has_active_focus(fs))
|
||||
set_focus_chain_active(oldt, false, false, true);
|
||||
|
||||
/* Then, remove the focus flag from the scope target and notify it */
|
||||
oldt->focused = 0;
|
||||
notify_focus_changed(oldt);
|
||||
}
|
||||
|
||||
fs->focus_scope_target = target;
|
||||
|
||||
if(target) {
|
||||
/* Now, give focus to the new scope target */
|
||||
target->focused = 1;
|
||||
notify_focus_changed(target);
|
||||
|
||||
if(jwidget_has_active_focus(fs))
|
||||
set_focus_chain_active(target, true, false, false);
|
||||
}
|
||||
|
||||
jevent e;
|
||||
e.source = fs;
|
||||
e.type = JWIDGET_FOCUS_TARGET_CHANGED;
|
||||
jwidget_event(fs, e);
|
||||
}
|
||||
|
||||
jwidget *jwidgetctx_enclosing_focus_scope(void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
do w = w->parent;
|
||||
while(w && w->focus_policy != J_FOCUS_POLICY_SCOPE);
|
||||
return w;
|
||||
}
|
||||
|
||||
void jwidgetctx_drop_focus(void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
if(!w || !jwidget_has_focus(w))
|
||||
return;
|
||||
jwidget *scope = jwidgetctx_enclosing_focus_scope(w);
|
||||
if(scope)
|
||||
jwidget_scope_clear_focus(scope);
|
||||
}
|
||||
|
||||
void jwidgetctx_grab_focus(void *w0)
|
||||
{
|
||||
J_CAST(w)
|
||||
if(!w || jwidget_has_focus(w))
|
||||
return;
|
||||
jwidget *scope = jwidgetctx_enclosing_focus_scope(w);
|
||||
if(scope)
|
||||
jwidget_scope_set_target(scope, w);
|
||||
}
|
||||
|
||||
//---
|
||||
// Event management
|
||||
//---
|
||||
|
@ -752,11 +868,3 @@ int j_register_event(void)
|
|||
event_id++;
|
||||
return event_id;
|
||||
}
|
||||
|
||||
__attribute__((constructor(1000)))
|
||||
static void j_register_jwidget(void)
|
||||
{
|
||||
JWIDGET_KEY = j_register_event();
|
||||
JWIDGET_FOCUS_IN = j_register_event();
|
||||
JWIDGET_FOCUS_OUT = j_register_event();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue