From 2227daeadec91f3fe4ae0145fb823c85af3dfd49 Mon Sep 17 00:00:00 2001 From: xianjimli Date: Wed, 20 Mar 2019 17:24:30 +0800 Subject: [PATCH] support use tab/up/down key to change focused edit --- docs/changes.md | 1 + src/base/widget.c | 76 +++++++++++++++++++++++++++++++++++++++ src/base/widget.h | 30 ++++++++++++++++ src/base/widget_consts.h | 6 ++++ src/base/window_manager.c | 3 -- src/widgets/edit.c | 10 ++++++ tests/edit_test.cc | 45 +++++++++++++++++++++++ 7 files changed, 168 insertions(+), 3 deletions(-) diff --git a/docs/changes.md b/docs/changes.md index e5ac3a201..462e6ef45 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -6,6 +6,7 @@ * 修改关闭system\_bar时没有注销事件的BUG(感谢朝泽提供补丁)。 * window支持fullscreen属性。 * 修改edit缺省焦点的BUG。 + * 支持用tab/up/down键切换编辑器焦点。 * 2019/03/19 * 增加window\_close\_force函数。 diff --git a/src/base/widget.c b/src/base/widget.c index cd907fbda..411b80273 100644 --- a/src/base/widget.c +++ b/src/base/widget.c @@ -2082,3 +2082,79 @@ bool_t widget_is_keyboard(widget_t* widget) { return FALSE; } + +static bool_t widget_is_focusable(widget_t* widget) { + value_t v; + value_set_bool(&v, FALSE); + widget_get_prop(widget, WIDGET_PROP_FOCUSABLE, &v); + + return value_bool(&v); +} + +static ret_t widget_on_visit_focusable(void* ctx, const void* data) { + widget_t* widget = WIDGET(data); + darray_t* all_focusable = (darray_t*)ctx; + + if (widget_is_focusable(widget)) { + darray_push(all_focusable, widget); + } + + return RET_OK; +} + +static ret_t widget_get_all_focusable_widgets_win(widget_t* widget, darray_t* all_focusable) { + widget_t* win = widget_get_window(widget); + return_value_if_fail(win != NULL, RET_BAD_PARAMS); + + widget_foreach(win, widget_on_visit_focusable, all_focusable); + + return RET_OK; +} + +static ret_t widget_move_focus(widget_t* widget, bool_t next) { + uint32_t i = 0; + uint32_t focus = 0; + darray_t all_focusable; + return_value_if_fail(widget != NULL && widget->focused, RET_BAD_PARAMS); + return_value_if_fail(darray_init(&all_focusable, 10, NULL, NULL) != NULL, RET_OOM); + + widget_get_all_focusable_widgets_win(widget, &all_focusable); + + if (all_focusable.size > 1) { + for (i = 0; i < all_focusable.size; i++) { + widget_t* iter = WIDGET(all_focusable.elms[i]); + if (iter == widget) { + if (next) { + if ((i + 1) == all_focusable.size) { + focus = 0; + } else { + focus = i + 1; + } + } else { + if (i == 0) { + focus = all_focusable.size - 1; + } else { + focus = i - 1; + } + } + + iter = WIDGET(all_focusable.elms[focus]); + widget_set_prop_bool(widget, WIDGET_PROP_FOCUS, FALSE); + widget_set_prop_bool(iter, WIDGET_PROP_FOCUS, TRUE); + break; + } + } + } + + darray_deinit(&all_focusable); + + return RET_OK; +} + +ret_t widget_focus_prev(widget_t* widget) { + return widget_move_focus(widget, FALSE); +} + +ret_t widget_focus_next(widget_t* widget) { + return widget_move_focus(widget, TRUE); +} diff --git a/src/base/widget.h b/src/base/widget.h index f331294de..5aa5356de 100644 --- a/src/base/widget.h +++ b/src/base/widget.h @@ -82,6 +82,12 @@ struct _widget_vtable_t { * 是否是窗口。 */ uint32_t is_window : 1; + /** + * 是否是focusable。 + * + *>如编辑器。 + */ + uint32_t is_focusable : 1; /** * 是否是设计窗口。 */ @@ -1683,6 +1689,30 @@ ret_t widget_update_style(widget_t* widget); */ ret_t widget_set_as_key_target(widget_t* widget); +/** + * @method widget_focus_next + * 把焦点移动下一个控件。 + * + *>widget必须是当前焦点控件。 + * @annotation ["private"] + * @param {widget_t*} widget widget对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t widget_focus_next(widget_t* widget); + +/** + * @method widget_focus_prev + * 把焦点移动前一个控件。 + * + *>widget必须是当前焦点控件。 + * @annotation ["private"] + * @param {widget_t*} widget widget对象。 + * + * @return {ret_t} 返回RET_OK表示成功,否则表示失败。 + */ +ret_t widget_focus_prev(widget_t* widget); + /** * @method widget_get_state_for_style * 把控件的状态转成获取style选要的状态,一般只在子类中使用。 diff --git a/src/base/widget_consts.h b/src/base/widget_consts.h index d1555db6a..e64b713d5 100644 --- a/src/base/widget_consts.h +++ b/src/base/widget_consts.h @@ -647,6 +647,12 @@ BEGIN_C_DECLS */ #define WIDGET_PROP_FOCUS "focus" +/** + * @const WIDGET_PROP_FOCUSABLE + * 是否支持焦点停留。 + */ +#define WIDGET_PROP_FOCUSABLE "focusable" + /** * @enum widget_type_t * @annotation ["scriptable", "string"] diff --git a/src/base/window_manager.c b/src/base/window_manager.c index 45b851ef1..35dbe1ed5 100644 --- a/src/base/window_manager.c +++ b/src/base/window_manager.c @@ -597,9 +597,6 @@ ret_t window_manager_on_paint_children(widget_t* widget, canvas_t* c) { widget_paint(iter, c); if (!has_fullscreen_win) { has_fullscreen_win = is_window_fullscreen(iter); - if (has_fullscreen_win) { - log_debug("%s is fullscreen\n", iter->name); - } } } } diff --git a/src/widgets/edit.c b/src/widgets/edit.c index 4b2184734..19e54c074 100644 --- a/src/widgets/edit.c +++ b/src/widgets/edit.c @@ -427,6 +427,12 @@ static ret_t edit_on_key_down(widget_t* widget, key_event_t* e) { } else { return edit_set_cursor_pos(widget, edit->cursor_pos + 1, edit->cursor_pos + 1); } + } else if (key == TK_KEY_TAB || key == TK_KEY_DOWN) { + widget_focus_next(widget); + return RET_OK; + } else if (key == TK_KEY_UP) { + widget_focus_prev(widget); + return RET_OK; } else { if (system_info()->app_type != APP_DESKTOP && key < 128 && isprint(key)) { return edit_input_char(widget, (wchar_t)key); @@ -844,6 +850,9 @@ ret_t edit_get_prop(widget_t* widget, const char* name, value_t* v) { } else if (tk_str_eq(name, WIDGET_PROP_TIPS)) { value_set_str(v, edit->tips); return RET_OK; + } else if (tk_str_eq(name, WIDGET_PROP_FOCUSABLE)) { + value_set_bool(v, !(edit->readonly)); + return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_VALUE)) { value_set_wstr(v, widget->text.str); return RET_OK; @@ -1225,6 +1234,7 @@ const char* s_edit_properties[] = {WIDGET_PROP_MIN, NULL}; TK_DECL_VTABLE(edit) = {.size = sizeof(edit_t), .type = WIDGET_TYPE_EDIT, + .is_focusable = TRUE, .clone_properties = s_edit_properties, .persistent_properties = s_edit_properties, .parent = TK_PARENT_VTABLE(widget), diff --git a/tests/edit_test.cc b/tests/edit_test.cc index 56233c3a3..113af83c9 100644 --- a/tests/edit_test.cc +++ b/tests/edit_test.cc @@ -164,3 +164,48 @@ TEST(Edit, focus) { widget_destroy(w); } + +TEST(Edit, focus_next) { + widget_t* w = window_create(NULL, 0, 0, 400, 400); + widget_t* g = group_box_create(w, 0, 0, 400, 400); + widget_t* e1 = edit_create(g, 10, 20, 30, 40); + widget_t* e2 = edit_create(g, 10, 60, 30, 40); + widget_t* e3 = edit_create(g, 10, 90, 30, 40); + + ASSERT_EQ(edit_set_focus(e1, TRUE), RET_OK); + ASSERT_EQ(g->key_target, e1); + ASSERT_EQ(widget_focus_next(e1), RET_OK); + ASSERT_EQ(g->key_target, e2); + ASSERT_EQ(widget_focus_next(e1), RET_BAD_PARAMS); + + ASSERT_EQ(widget_focus_next(e2), RET_OK); + ASSERT_EQ(g->key_target, e3); + + ASSERT_EQ(widget_focus_next(e3), RET_OK); + ASSERT_EQ(g->key_target, e1); + + widget_destroy(w); +} + +TEST(Edit, focus_prev) { + widget_t* w = window_create(NULL, 0, 0, 400, 400); + widget_t* g = group_box_create(w, 0, 0, 400, 400); + widget_t* e1 = edit_create(g, 10, 20, 30, 40); + widget_t* e2 = edit_create(g, 10, 60, 30, 40); + widget_t* e3 = edit_create(g, 10, 90, 30, 40); + + ASSERT_EQ(edit_set_focus(e1, TRUE), RET_OK); + ASSERT_EQ(g->key_target, e1); + + ASSERT_EQ(widget_focus_prev(e1), RET_OK); + ASSERT_EQ(g->key_target, e3); + ASSERT_EQ(widget_focus_prev(e1), RET_BAD_PARAMS); + + ASSERT_EQ(widget_focus_prev(e3), RET_OK); + ASSERT_EQ(g->key_target, e2); + + ASSERT_EQ(widget_focus_prev(e2), RET_OK); + ASSERT_EQ(g->key_target, e1); + + widget_destroy(w); +}