/** * File: edit.h * Author: AWTK Develop Team * Brief: edit * * Copyright (c) 2018 - 2023 Guangzhou ZHIYUAN Electronics Co.,Ltd. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * License file for more details. * */ /** * History: * ================================================================ * 2018-04-05 Li XianJing created * 2018-11-20 Li YaoShen 1.text selection; 2.cursor movable * 2018-06-24 Li XianJing rework with stb_text_edit * */ #include "tkc/mem.h" #include "tkc/utf8.h" #include "widgets/edit.h" #include "base/keys.h" #include "base/idle.h" #include "base/timer.h" #include "tkc/utils.h" #include "tkc/time_now.h" #include "base/enums.h" #include "base/events.h" #include "base/clip_board.h" #include "base/window_manager.h" #include "widgets/edit_ipv4.h" #include "widgets/edit_date.h" #include "widgets/edit_time.h" #include "widgets/edit_time_full.h" #define ACTION_TEXT_NEXT "next" #define ACTION_TEXT_DONE "done" #define PASSWORD_MASK_CHAR '*' static ret_t edit_auto_fix(widget_t* widget); static ret_t edit_reset_layout(widget_t* widget); static ret_t edit_update_status(widget_t* widget); static ret_t edit_check_valid_value(widget_t* widget); static ret_t edit_select_all_async(const idle_info_t* info); static ret_t edit_dispatch_value_change_event(widget_t* widget, event_type_t type); static ret_t edit_paste(widget_t* widget, const wchar_t* str, uint32_t size); static ret_t edit_save_text(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->cancelable) { wstr_set(&(edit->saved_text), widget->text.str); } return RET_OK; } static ret_t edit_rollback_text(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->cancelable) { wstr_set(&(edit->last_changing_text), widget->text.str); widget_set_text(widget, edit->saved_text.str); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); } return RET_OK; } static ret_t edit_commit_text(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->cancelable) { wstr_set(&(edit->saved_text), widget->text.str); edit_update_status(widget); } return RET_OK; } static ret_t edit_dispatch_value_change_event(widget_t* widget, event_type_t type) { value_change_event_t evt; edit_t* edit = EDIT(widget); wstr_t* text = &(widget->text); wstr_t* last = &(edit->last_changing_text); if (type == EVT_VALUE_CHANGED) { last = &(edit->last_changed_text); } if ((last->size == 0 && text->size == 0) || wstr_equal(last, text)) { return RET_NOT_MODIFIED; } value_change_event_init(&evt, type, widget); value_set_wstr(&(evt.old_value), last->str); value_set_wstr(&(evt.new_value), text->str); widget_dispatch(widget, (event_t*)&evt); if (type == EVT_VALUE_CHANGED) { wstr_set(last, text->str); } return RET_OK; } static ret_t edit_update_caret(const timer_info_t* timer) { edit_t* edit = NULL; widget_t* widget = NULL; return_value_if_fail(timer != NULL, RET_REMOVE); edit = EDIT(timer->ctx); widget = WIDGET(timer->ctx); return_value_if_fail(edit != NULL && widget != NULL, RET_REMOVE); if (edit->readonly) { edit->timer_id = TK_INVALID_ID; text_edit_set_caret_visible(edit->model, FALSE); return RET_REMOVE; } widget_invalidate_force(widget, NULL); text_edit_invert_caret_visible(edit->model); if (widget->focused) { return RET_REPEAT; } else { edit->timer_id = TK_INVALID_ID; text_edit_set_caret_visible(edit->model, FALSE); return RET_REMOVE; } } static ret_t edit_start_update_caret(edit_t* edit) { #define UPDATE_CARET_TIME 600 if (edit->readonly) { text_edit_set_caret_visible(edit->model, FALSE); } else { if (edit->timer_id == TK_INVALID_ID) { edit->timer_id = timer_add(edit_update_caret, WIDGET(edit), UPDATE_CARET_TIME); } else { timer_reset(edit->timer_id); } text_edit_set_caret_visible(edit->model, TRUE); } return RET_OK; } ret_t edit_on_paint_self(widget_t* widget, canvas_t* c) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->input_type != INPUT_PASSWORD && edit->input_type != INPUT_CUSTOM_PASSWORD) { text_edit_set_mask(edit->model, FALSE); } return text_edit_paint(edit->model, c); } static bool_t edit_is_valid_char_default(widget_t* widget, wchar_t c) { wstr_t tmp; bool_t ret = FALSE; wstr_t* text = NULL; text_edit_state_t state; uint32_t cursor_pos = 0; edit_t* edit = EDIT(widget); input_type_t input_type = (input_type_t)0; return_value_if_fail(widget != NULL && edit != NULL, FALSE); wstr_init(&tmp, 0); text_edit_get_state(edit->model, &state); cursor_pos = state.cursor; if (state.select_start != state.select_end) { uint32_t end = state.select_start > state.select_end ? state.select_start : state.select_end; cursor_pos = state.select_start < state.select_end ? state.select_start : state.select_end; text = &tmp; wstr_set(&tmp, widget->text.str); wstr_remove(&tmp, cursor_pos, end - cursor_pos); } else { text = &(widget->text); } input_type = edit->input_type; switch (input_type) { case INPUT_INT: case INPUT_UINT: { if (text->size >= TK_NUM_MAX_LEN) { break; } else if (c >= '0' && c <= '9') { ret = TRUE; break; } else if (c == '+' || (c == '-' && input_type == INPUT_INT)) { if (cursor_pos == 0) { ret = TRUE; } break; } break; } case INPUT_FLOAT: case INPUT_UFLOAT: { if (text->size >= TK_NUM_MAX_LEN) { break; } else if (c >= '0' && c <= '9') { ret = TRUE; break; } else if (c == '+' || (c == '-' && input_type == INPUT_FLOAT)) { if (cursor_pos == 0) { ret = TRUE; } break; } else if (c == '.' || c == 'e') { if (cursor_pos > 0 && wcs_chr(text->str, c) == NULL) { ret = TRUE; } } break; } case INPUT_EMAIL: { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.' || c == '_' || c == ' ') { ret = TRUE; } else if (c == '@') { if (cursor_pos > 0 && wcs_chr(text->str, c) == NULL) { ret = TRUE; } } break; } case INPUT_HEX: { if (text->size > 10) { break; } else if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) { ret = TRUE; } else if (c == 'X' || c == 'x') { if (cursor_pos == 1 && text->str[0] == '0') { ret = TRUE; } } break; } case INPUT_PHONE: { if (c >= '0' && c <= '9') { ret = TRUE; break; } else if (c == '-') { if (cursor_pos > 0 && wcs_chr(text->str, c) == NULL) { ret = TRUE; } } break; } default: { ret = TRUE; break; } } switch (edit->input_type) { case INPUT_ASCII: case INPUT_PHONE: case INPUT_EMAIL: case INPUT_TEXT: case INPUT_CUSTOM: case INPUT_CUSTOM_PASSWORD: case INPUT_PASSWORD: { if (text->size >= edit->max) { ret = FALSE; } } default: break; } wstr_reset(&tmp); return ret; } bool_t edit_is_valid_char(widget_t* widget, wchar_t c) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, FALSE); if (edit->is_valid_char != NULL) { return edit->is_valid_char(widget, c); } else { return edit_is_valid_char_default(widget, c); } } static bool_t edit_has_selection(widget_t* widget) { text_edit_state_t state; edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, FALSE); text_edit_get_state(edit->model, &state); return state.select_start != state.select_end; } ret_t edit_input_char(widget_t* widget, wchar_t c) { return edit_paste(widget, &c, 1); } static ret_t edit_on_text_edit_text_will_delete(void* ctx, delete_type_t delete_type) { widget_t* widget = WIDGET(ctx); edit_t* edit = EDIT(widget); ret_t ret = RET_OK; uint32_t size1 = 0; uint32_t size2 = 0; return_value_if_fail(edit != NULL, RET_BAD_PARAMS); size1 = widget->text.size; if (edit->pre_delete != NULL) { ret = edit->pre_delete(widget, delete_type); } size2 = widget->text.size; edit->is_text_deleted = size1 != size2; return ret; } static ret_t edit_on_text_edit_char_will_input(void* ctx, wchar_t c) { widget_t* widget = WIDGET(ctx); edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit_is_valid_char(widget, c)) { if (edit->is_text_deleted) { /*selection was cleared by edit->pre_delete, should not overwrite the next char*/ text_edit_unselect(edit->model); edit->is_text_deleted = FALSE; } return RET_OK; } return RET_STOP; } static ret_t edit_commit_str(widget_t* widget, const char* str) { wchar_t wstr[32] = {0}; tk_utf8_to_utf16(str, wstr, ARRAY_SIZE(wstr)); return edit_paste(widget, wstr, wcslen(wstr)); } static bool_t edit_is_number(widget_t* widget) { edit_t* edit = EDIT(widget); input_type_t input_type = (input_type_t)0; return_value_if_fail(widget != NULL && edit != NULL, FALSE); input_type = edit->input_type; return input_type == INPUT_UINT || input_type == INPUT_INT || input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT || input_type == INPUT_HEX; } static bool_t edit_is_size_valid(widget_t* widget) { edit_t* edit = EDIT(widget); uint32_t size = widget->text.size; uint32_t min = (uint32_t)(edit->min); uint32_t max = (uint32_t)(edit->max); if (min == max && min == 0) { return TRUE; } return (min <= size && size <= max); } static bool_t edit_is_valid_value_default(widget_t* widget) { wstr_t* text = NULL; edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, FALSE); text = &(widget->text); switch (edit->input_type) { case INPUT_ASCII: case INPUT_PHONE: case INPUT_EMAIL: case INPUT_TEXT: case INPUT_PASSWORD: { if (edit_is_size_valid(widget)) { if (edit->input_type == INPUT_EMAIL) { return wstr_count_char(text, '@') == 1; } return TRUE; } return FALSE; } case INPUT_INT: case INPUT_UINT: { int32_t v = 0; int32_t min = (int32_t)(edit->min); int32_t max = (int32_t)(edit->max); if (text->size == 0 || text->size > 11) { return FALSE; } if (min == max) { return TRUE; } wstr_to_int(text, &v); if (text->size >= 10) { wstr_t str; bool_t result = FALSE; wstr_init(&str, 32); wstr_from_int(&str, v); result = wstr_equal(&str, text); wstr_reset(&str); if (!result) { return FALSE; } } return min <= v && v <= max; } case INPUT_FLOAT: case INPUT_UFLOAT: { double v = 0; double min = edit->min; double max = edit->max; if (text->size == 0) { return FALSE; } if (min == max) { return TRUE; } wstr_to_float(text, &v); return min <= v && v <= max; } default: break; } return TRUE; } static ret_t edit_auto_fix_default(widget_t* widget) { wstr_t* text = NULL; edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); text = &(widget->text); switch (edit->input_type) { case INPUT_TEXT: { uint32_t size = text->size; uint32_t max = edit->max; if (size > max) { text->size = max; text->str[max] = 0; } break; } case INPUT_INT: { int32_t v = 0; int32_t min = (int32_t)(edit->min); int32_t max = (int32_t)(edit->max); wstr_to_int(text, &v); if (v < min) { v = min; } if (v > max) { v = max; } wstr_from_int(text, v); break; } case INPUT_UINT: { uint64_t v = 0; uint32_t min = (uint32_t)(edit->min); uint32_t max = (uint32_t)(edit->max); wstr_to_int64(text, &v); if (v < min) { v = min; } if (v > max) { v = max; } wstr_from_int64(text, v); break; } case INPUT_FLOAT: case INPUT_UFLOAT: { double v = 0; double min = edit->min; double max = edit->max; wstr_to_float(text, &v); if (v < min) { v = min; } if (v > max) { v = max; } wstr_from_float(text, v); wstr_trim_float_zero(text); break; } default: break; } text_edit_set_cursor(edit->model, 0xffffffff); return RET_OK; } static ret_t edit_update_status(widget_t* widget) { edit_t* edit = EDIT(widget); if (widget->text.size == 0) { if (widget->focused) { widget_set_state(widget, WIDGET_STATE_EMPTY_FOCUS); } else { widget_set_state(widget, WIDGET_STATE_EMPTY); } } else { if (edit->cancelable) { if (widget->loading) { edit_save_text(widget); } else { if (!wstr_equal(&(edit->saved_text), &(widget->text))) { widget_set_state(widget, WIDGET_STATE_CHANGED); return RET_OK; } } } if (widget->focused) { widget_set_state(widget, WIDGET_STATE_FOCUSED); } else if (edit->is_text_error) { widget_set_state(widget, WIDGET_STATE_ERROR); } else { widget_set_state(widget, WIDGET_STATE_NORMAL); } } return RET_OK; } static ret_t edit_do_request_input_method(widget_t* widget) { edit_t* edit = EDIT(widget); input_method_t* im = input_method(); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->readonly) { return RET_OK; } input_method_request(im, widget); if (edit->action_text != NULL) { const char* action_text = locale_info_tr(widget_get_locale_info(widget), edit->action_text); input_method_update_action_button_info(im, action_text, TRUE); } return RET_OK; } static ret_t edit_request_input_method_on_window_open(void* ctx, event_t* e) { edit_do_request_input_method(WIDGET(ctx)); return RET_REMOVE; } static ret_t edit_request_input_method(widget_t* widget) { if (widget_is_window_opened(widget)) { edit_do_request_input_method(widget); } else { widget_t* win = widget_get_window(widget); if (win != NULL) { widget_on(win, EVT_WINDOW_OPEN, edit_request_input_method_on_window_open, widget); } } return RET_OK; } static ret_t edit_on_blur(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->close_im_when_blured) { edit->is_key_inputing = FALSE; input_method_request(input_method(), NULL); } edit_update_status(widget); edit_check_valid_value(widget); text_edit_preedit_confirm(edit->model); text_edit_unselect(edit->model); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGED); edit_commit_text(widget); return RET_OK; } static ret_t edit_focus_request_input_method(const idle_info_t* info) { edit_t* edit = EDIT(info->ctx); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->open_im_when_focused) { edit_request_input_method(WIDGET(edit)); } return RET_REMOVE; } static ret_t edit_on_focused(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit_start_update_caret(edit); if (widget->target == NULL) { widget_add_idle(widget, edit_focus_request_input_method); if (!edit->select_none_when_focused && !edit->readonly) { widget_add_idle(widget, edit_select_all_async); } } edit_update_status(widget); return RET_OK; } static ret_t edit_pointer_up_cleanup(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && widget != NULL, RET_BAD_PARAMS); widget_ungrab(widget->parent, widget); widget_set_state(widget, WIDGET_STATE_NORMAL); return RET_OK; } static ret_t edit_paste(widget_t* widget, const wchar_t* str, uint32_t size) { edit_t* edit = EDIT(widget); ret_t ret = RET_BAD_PARAMS; return_value_if_fail(edit != NULL, RET_BAD_PARAMS); wstr_set(&(edit->last_changing_text), widget->text.str); ret = text_edit_paste(edit->model, str, size); if (ret == RET_OK) { edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); } return ret; } static ret_t edit_pre_input(widget_t* widget, uint32_t key) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->pre_input != NULL) { return edit->pre_input(widget, key); } return RET_OK; } static ret_t edit_on_key_down(widget_t* widget, key_event_t* e) { uint32_t key = e->key; edit_t* edit = EDIT(widget); #ifdef MACOS bool_t is_control = e->cmd; #else bool_t is_control = e->ctrl; #endif bool_t is_print = key < 128 && tk_isprint(key); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (!is_control) { if (edit_pre_input(widget, key) == RET_STOP) { return RET_STOP; } } if (key == TK_KEY_TAB || key == TK_KEY_ESCAPE || (key >= TK_KEY_F1 && key <= TK_KEY_F12)) { return RET_OK; } else if (key == TK_KEY_DOWN) { if (widget_is_change_focus_key(widget, e)) { return RET_OK; } if (edit_is_number(widget) || edit->inc_value != NULL) { edit_dec(edit); } else { widget_focus_next(widget); } return RET_STOP; } else if (key == TK_KEY_UP) { if (widget_is_change_focus_key(widget, e)) { return RET_OK; } if (edit_is_number(widget) || edit->inc_value != NULL) { edit_inc(edit); } else { widget_focus_prev(widget); } return RET_STOP; } else if (key == TK_KEY_LEFT || key == TK_KEY_RIGHT || key == TK_KEY_HOME || key == TK_KEY_END) { text_edit_key_down(edit->model, e); } else if (is_print || key == TK_KEY_BACKSPACE || key == TK_KEY_DELETE) { wstr_set(&(edit->last_changing_text), widget->text.str); text_edit_key_down(edit->model, e); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); } else { if (widget->emitter != NULL) { void* saved_target = e->e.target; e->e.target = widget; emitter_dispatch(widget->emitter, (event_t*)e); e->e.target = saved_target; } } return RET_STOP; } static ret_t edit_on_key_up(widget_t* widget, key_event_t* e) { int key = e->key; ret_t ret = RET_OK; edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (key_code_is_enter(key)) { if (edit->timer_id == TK_INVALID_ID) { edit_on_focused(widget); } else { keyboard_type_t keyboard_type = system_info()->keyboard_type; if (keyboard_type != KEYBOARD_3KEYS && keyboard_type != KEYBOARD_5KEYS) { widget_focus_next(widget); widget_set_focused(widget, FALSE); } } ret = RET_STOP; } else { ret = text_edit_key_up(edit->model, e); } return ret; } static ret_t edit_select_all_async(const idle_info_t* info) { edit_t* edit = EDIT(info->ctx); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); text_edit_select_all(edit->model); if (edit->fix_value != NULL) { text_edit_set_cursor(edit->model, 0); } return RET_REMOVE; } static ret_t edit_check_valid_value(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); edit->is_text_error = FALSE; if (!edit_is_valid_value(widget)) { if (edit->auto_fix) { edit_auto_fix(widget); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGED); if (widget->text.size > 0 && !edit_is_valid_value(widget)) { edit->is_text_error = TRUE; widget_set_state(widget, WIDGET_STATE_ERROR); } } else if (widget->text.size > 0) { edit->is_text_error = TRUE; widget_set_state(widget, WIDGET_STATE_ERROR); } } return RET_OK; } ret_t edit_on_event(widget_t* widget, event_t* e) { ret_t ret = RET_OK; uint32_t type = e->type; edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); if (!widget->visible) { return RET_OK; } switch (type) { case EVT_POINTER_DOWN: { pointer_event_t evt = *(pointer_event_t*)e; if (widget_find_target(widget, evt.x, evt.y) == NULL) { text_edit_click(edit->model, evt.x, evt.y); widget_grab(widget->parent, widget); } if (widget->target == NULL && !edit->readonly) { input_method_request(input_method(), widget); } edit_update_status(widget); widget_invalidate(widget, NULL); edit_start_update_caret(edit); break; } case EVT_POINTER_DOWN_ABORT: { edit_pointer_up_cleanup(widget); widget_invalidate(widget, NULL); break; } case EVT_POINTER_MOVE: { pointer_event_t evt = *(pointer_event_t*)e; if (widget->parent && widget->parent->grab_widget == widget) { text_edit_drag(edit->model, evt.x, evt.y); ret = RET_STOP; } widget_invalidate(widget, NULL); break; } case EVT_POINTER_UP: { ret = RET_STOP; widget_ungrab(widget->parent, widget); widget_invalidate(widget, NULL); break; } case EVT_KEY_DOWN: { key_event_t* evt = (key_event_t*)e; int32_t key = evt->key; #ifdef MACOS bool_t is_control = evt->cmd; #else bool_t is_control = evt->ctrl; #endif if (edit->readonly) { if (is_control && (key == TK_KEY_C || key == TK_KEY_c)) { log_debug("copy\n"); } else if (key == TK_KEY_DOWN || key == TK_KEY_UP) { log_debug("key down or key up\n"); } else { break; } } edit->is_key_inputing = TRUE; ret = edit_on_key_down(widget, evt); edit_update_status(widget); widget_invalidate(widget, NULL); edit_start_update_caret(edit); break; } case EVT_KEY_UP: { edit->is_key_inputing = TRUE; ret = edit_on_key_up(widget, (key_event_t*)e); widget_invalidate(widget, NULL); break; } case EVT_IM_COMMIT: { text_edit_state_t state; text_edit_get_state(edit->model, &state); im_commit_event_t* evt = (im_commit_event_t*)e; if (state.preedit) { text_edit_preedit_clear(edit->model); } if (evt->replace) { edit_clear(edit); } edit_commit_str(widget, evt->text); widget_invalidate(widget, NULL); break; } case EVT_IM_PREEDIT: { text_edit_preedit(edit->model); break; } case EVT_IM_PREEDIT_CONFIRM: { text_edit_preedit_confirm(edit->model); break; } case EVT_IM_PREEDIT_ABORT: { wstr_set(&(edit->last_changing_text), widget->text.str); text_edit_preedit_abort(edit->model); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); break; } case EVT_BLUR: { edit_on_blur(widget); break; } case EVT_FOCUS: { if (edit->open_im_when_focused) { edit_on_focused(widget); } edit_save_text(widget); break; } case EVT_WHEEL: { wheel_event_t* evt = (wheel_event_t*)e; int32_t delta = evt->dy; if (delta > 0) { edit_dec(edit); } else if (delta < 0) { edit_inc(edit); } ret = RET_STOP; break; } case EVT_RESIZE: case EVT_MOVE_RESIZE: { edit_reset_layout(widget); break; } case EVT_VALUE_CHANGING: { edit_update_status(widget); widget_invalidate(widget, NULL); break; } case EVT_IM_CLEAR: { edit_clear(edit); break; } case EVT_IM_CANCEL: { edit_rollback_text(widget); break; } case EVT_IM_ACTION: { if (tk_str_eq(edit->action_text, ACTION_TEXT_DONE)) { edit->is_key_inputing = FALSE; edit_check_valid_value(widget); input_method_request(input_method(), NULL); text_edit_preedit_confirm(edit->model); text_edit_unselect(edit->model); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGED); edit_commit_text(widget); } else if (tk_str_eq(edit->action_text, ACTION_TEXT_NEXT)) { widget_focus_next(widget); } log_debug("action button:%s\n", edit->action_text); break; } case EVT_POINTER_LEAVE: { edit_update_status(widget); break; } case EVT_POINTER_ENTER: { if (widget->text.size == 0) { widget_set_state(widget, WIDGET_STATE_EMPTY_OVER); } else { widget_set_state(widget, WIDGET_STATE_OVER); } break; } case EVT_CONTEXT_MENU: { pointer_event_t* evt = (pointer_event_t*)e; point_t p = {evt->x, evt->y}; widget_to_local(widget, &p); widget_to_screen(widget, &p); text_edit_show_context_menu(edit->model, p.x, p.y); break; } default: break; } return ret; } static ret_t edit_on_re_translate(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->tr_tips != NULL) { const char* tr_tips = locale_info_tr(widget_get_locale_info(widget), edit->tr_tips); edit_set_tips(widget, tr_tips); } return RET_OK; } ret_t edit_set_text_limit(widget_t* widget, uint32_t min, uint32_t max) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->min = min; edit->max = max; edit->input_type = INPUT_TEXT; return RET_OK; } ret_t edit_set_int_limit(widget_t* widget, int32_t min, int32_t max, uint32_t step) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->min = min; edit->max = max; edit->step = step; if (edit->input_type != INPUT_UINT) { edit->input_type = INPUT_INT; } return RET_OK; } ret_t edit_set_float_limit(widget_t* widget, double min, double max, double step) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->min = min; edit->max = max; edit->step = step; if (edit->input_type != INPUT_UFLOAT) { edit->input_type = INPUT_FLOAT; } return RET_OK; } ret_t edit_set_readonly(widget_t* widget, bool_t readonly) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->readonly = readonly; return RET_OK; } ret_t edit_set_cancelable(widget_t* widget, bool_t cancelable) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->cancelable = cancelable; return RET_OK; } ret_t edit_set_auto_fix(widget_t* widget, bool_t auto_fix) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->auto_fix = auto_fix; return RET_OK; } ret_t edit_set_select_none_when_focused(widget_t* widget, bool_t select_none_when_focused) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->select_none_when_focused = select_none_when_focused; return RET_OK; } ret_t edit_set_open_im_when_focused(widget_t* widget, bool_t open_im_when_focused) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->open_im_when_focused = open_im_when_focused; return RET_OK; } ret_t edit_set_close_im_when_blured(widget_t* widget, bool_t close_im_when_blured) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->close_im_when_blured = close_im_when_blured; return RET_OK; } ret_t edit_set_input_type(widget_t* widget, input_type_t type) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->input_type = type; edit->pre_input = NULL; edit->pre_delete = NULL; edit->is_valid_char = NULL; edit->fix_value = NULL; edit->inc_value = NULL; edit->dec_value = NULL; edit->is_valid_value = NULL; if (edit->step == 0.0 && (type == INPUT_INT || type == INPUT_UINT)) { edit->step = 1; } else if (edit->step == 0.0 && (type == INPUT_FLOAT || type == INPUT_UFLOAT)) { edit->step = 1.0f; } else if (type == INPUT_PASSWORD) { edit_set_password_visible(widget, edit->password_visible); } else if (type == INPUT_IPV4) { edit->fix_value = edit_ipv4_fix; edit->inc_value = edit_ipv4_inc_value; edit->dec_value = edit_ipv4_dec_value; edit->pre_input = edit_ipv4_pre_input; edit->is_valid_value = edit_ipv4_is_valid; edit->is_valid_char = edit_ipv4_is_valid_char; edit->pre_delete = edit_ipv4_pre_delete; } else if (type == INPUT_DATE) { edit->fix_value = edit_date_fix; edit->inc_value = edit_date_inc_value; edit->dec_value = edit_date_dec_value; edit->pre_input = edit_date_pre_input; edit->is_valid_value = edit_date_is_valid; edit->is_valid_char = edit_date_is_valid_char; edit->pre_delete = edit_date_pre_delete; } else if (type == INPUT_TIME) { edit->fix_value = edit_time_fix; edit->inc_value = edit_time_inc_value; edit->dec_value = edit_time_dec_value; edit->pre_input = edit_time_pre_input; edit->is_valid_value = edit_time_is_valid; edit->is_valid_char = edit_time_is_valid_char; edit->pre_delete = edit_time_pre_delete; } else if (type == INPUT_TIME_FULL) { edit->fix_value = edit_time_full_fix; edit->inc_value = edit_time_full_inc_value; edit->dec_value = edit_time_full_dec_value; edit->pre_input = edit_time_full_pre_input; edit->is_valid_value = edit_time_full_is_valid; edit->is_valid_char = edit_time_full_is_valid_char; edit->pre_delete = edit_time_full_pre_delete; } return RET_OK; } ret_t edit_set_tips(widget_t* widget, const char* tips) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && tips != NULL, RET_BAD_PARAMS); edit->tips = tk_str_copy(edit->tips, tips); text_edit_set_tips(edit->model, edit->tips, FALSE); return RET_OK; } ret_t edit_set_action_text(widget_t* widget, const char* action_text) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->action_text = tk_str_copy(edit->action_text, action_text); return RET_OK; } static ret_t edit_apply_tr_text_before_paint(void* ctx, event_t* e) { widget_t* widget = WIDGET(ctx); edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->tr_tips != NULL) { const char* tr_tips = locale_info_tr(widget_get_locale_info(widget), edit->tr_tips); edit_set_tips(widget, tr_tips); } return RET_REMOVE; } static ret_t edit_reset_layout(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); text_edit_layout(edit->model); widget_invalidate(widget, NULL); return RET_OK; } ret_t edit_set_tr_tips(widget_t* widget, const char* tr_tips) { edit_t* edit = EDIT(widget); widget_t* win = widget_get_window(widget); return_value_if_fail(edit != NULL && tr_tips != NULL, RET_BAD_PARAMS); if (*tr_tips == '\0') { TKMEM_FREE(edit->tr_tips); return RET_OK; } edit->tr_tips = tk_str_copy(edit->tr_tips, tr_tips); if (win != NULL) { tr_tips = locale_info_tr(widget_get_locale_info(widget), tr_tips); edit_set_tips(widget, tr_tips); } else { widget_on(widget, EVT_BEFORE_PAINT, edit_apply_tr_text_before_paint, widget); } return RET_OK; } ret_t edit_set_keyboard(widget_t* widget, const char* keyboard) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && keyboard != NULL, RET_BAD_PARAMS); edit->keyboard = tk_str_copy(edit->keyboard, keyboard); return RET_OK; } static bool_t widget_has_uint_min_max(widget_t* widget) { edit_t* edit = EDIT(widget); input_type_t input_type = INPUT_TEXT; return_value_if_fail(edit != NULL, FALSE); input_type = edit->input_type; return input_type == INPUT_TEXT || input_type == INPUT_ASCII || input_type == INPUT_PASSWORD || input_type == INPUT_EMAIL || input_type == INPUT_UINT; } ret_t edit_get_prop(widget_t* widget, const char* name, value_t* v) { edit_t* edit = EDIT(widget); input_type_t input_type = INPUT_TEXT; return_value_if_fail(edit != NULL && name != NULL && v != NULL, RET_BAD_PARAMS); input_type = edit->input_type; if (tk_str_eq(name, WIDGET_PROP_MIN)) { if (input_type == INPUT_INT) { value_set_int(v, edit->min); } else if (widget_has_uint_min_max(widget)) { value_set_uint32(v, edit->min); } else if (input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { value_set_double(v, edit->min); } else { return RET_NOT_FOUND; } return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_MAX)) { if (input_type == INPUT_INT) { value_set_int(v, edit->max); } else if (widget_has_uint_min_max(widget)) { value_set_uint32(v, edit->max); } else if (input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { value_set_double(v, edit->max); } else { return RET_NOT_FOUND; } return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_STEP)) { if (input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { value_set_float(v, edit->step); return RET_OK; } else if (input_type == INPUT_INT || input_type == INPUT_UINT) { value_set_double(v, edit->step); return RET_OK; } else { return RET_NOT_FOUND; } } else if (tk_str_eq(name, WIDGET_PROP_INPUT_TYPE)) { value_set_uint32(v, input_type); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_READONLY)) { value_set_bool(v, edit->readonly); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CANCELABLE)) { value_set_bool(v, edit->cancelable); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_AUTO_FIX)) { value_set_bool(v, edit->auto_fix); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_SELECT_NONE_WHEN_FOCUSED)) { value_set_bool(v, edit->select_none_when_focused); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_OPEN_IM_WHEN_FOCUSED)) { value_set_bool(v, edit->open_im_when_focused); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CLOSE_IM_WHEN_BLURED)) { value_set_bool(v, edit->close_im_when_blured); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_LEFT_MARGIN)) { uint32_t margin = 0; if (widget->astyle != NULL) { TEXT_EDIT_GET_STYLE_MARGIN(widget->astyle, margin, LEFT); } if (margin == 0) { margin = edit->left_margin != 0 ? edit->left_margin : edit->margin; } value_set_int(v, margin); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_RIGHT_MARGIN)) { uint32_t margin = 0; if (widget->astyle != NULL) { TEXT_EDIT_GET_STYLE_MARGIN(widget->astyle, margin, RIGHT); } if (margin == 0) { margin = edit->right_margin != 0 ? edit->right_margin : edit->margin; } value_set_int(v, margin); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_TOP_MARGIN)) { uint32_t margin = 0; if (widget->astyle != NULL) { TEXT_EDIT_GET_STYLE_MARGIN(widget->astyle, margin, TOP); } if (margin == 0) { margin = edit->top_margin != 0 ? edit->top_margin : edit->margin; } value_set_int(v, margin); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_BOTTOM_MARGIN)) { uint32_t margin = 0; if (widget->astyle != NULL) { TEXT_EDIT_GET_STYLE_MARGIN(widget->astyle, margin, BOTTOM); } if (margin == 0) { margin = edit->bottom_margin != 0 ? edit->bottom_margin : edit->margin; } value_set_int(v, margin); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_PASSWORD_VISIBLE)) { value_set_bool(v, edit->password_visible); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_ACTION_TEXT)) { value_set_str(v, edit->action_text); return RET_OK; } 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_TR_TIPS)) { value_set_str(v, edit->tr_tips); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_KEYBOARD)) { value_set_str(v, edit->keyboard); 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)) { switch (edit->input_type) { case INPUT_INT: { int32_t n = edit_get_int(widget); value_set_int32(v, n); break; } case INPUT_UINT: { uint32_t n = (uint32_t)edit_get_int(widget); value_set_uint32(v, n); break; } case INPUT_FLOAT: case INPUT_UFLOAT: { double d = edit_get_double(widget); value_set_double(v, d); break; } default: { value_set_wstr(v, widget->text.str); } } return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CARET_X)) { text_edit_state_t state; text_edit_get_state(edit->model, &state); value_set_int(v, state.caret.x); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CARET_Y)) { text_edit_state_t state; text_edit_get_state(edit->model, &state); value_set_int(v, state.caret.y); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_LINE_HEIGHT)) { text_edit_state_t state; text_edit_get_state(edit->model, &state); value_set_int(v, state.line_height); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_INPUTING)) { input_method_t* im = input_method(); bool_t inputing = (im != NULL && im->widget == widget) || edit->is_key_inputing; /* 当控件没有父集窗口或者父集窗口没有打开的时候,通过 focused 来判断是否正在输入 */ if (!inputing && !widget_is_window_opened(widget)) { inputing = widget->focused; } value_set_bool(v, inputing); return RET_OK; } return RET_NOT_FOUND; } static ret_t edit_set_text(widget_t* widget, const value_t* v) { wstr_t str; wstr_init(&str, 0); edit_t* edit = EDIT(widget); return_value_if_fail(wstr_from_value(&str, v) == RET_OK, RET_BAD_PARAMS); if (!wstr_equal(&(widget->text), &str)) { uint32_t len = str.size; input_type_t input_type = edit->input_type; if (input_type == INPUT_INT || input_type == INPUT_UINT || input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { len = tk_min(str.size, 64); } else { len = edit->max > 0 ? tk_min(str.size, edit->max) : str.size; } wstr_set_with_len(&(widget->text), str.str, len); text_edit_set_cursor(edit->model, widget->text.size); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGED); edit_update_status(widget); edit_check_valid_value(widget); } wstr_reset(&str); return widget_invalidate_force(widget, NULL); } ret_t edit_set_prop(widget_t* widget, const char* name, const value_t* v) { edit_t* edit = EDIT(widget); input_type_t input_type = INPUT_TEXT; return_value_if_fail(edit != NULL && name != NULL && v != NULL, RET_BAD_PARAMS); input_type = edit->input_type; if (tk_str_eq(name, WIDGET_PROP_MIN)) { if (input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { edit->min = value_double(v); } else { edit->min = value_int(v); } return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_MAX)) { if (input_type == INPUT_FLOAT || input_type == INPUT_UFLOAT) { edit->max = value_double(v); } else { edit->max = value_int64(v); } return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_STEP)) { edit->step = value_double(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_INPUT_TYPE)) { if (v->type == VALUE_TYPE_STRING) { const key_type_value_t* kv = input_type_find(value_str(v)); if (kv != NULL) { input_type = (input_type_t)(kv->value); } } else { input_type = (input_type_t)value_int(v); } edit_set_input_type(widget, input_type); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_READONLY)) { edit->readonly = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CANCELABLE)) { edit->cancelable = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_AUTO_FIX)) { edit->auto_fix = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_SELECT_NONE_WHEN_FOCUSED)) { edit->select_none_when_focused = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_OPEN_IM_WHEN_FOCUSED)) { edit->open_im_when_focused = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_CLOSE_IM_WHEN_BLURED)) { edit->close_im_when_blured = value_bool(v); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_MARGIN)) { edit->margin = value_int(v); edit_reset_layout(widget); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_LEFT_MARGIN)) { edit->left_margin = value_int(v); edit_reset_layout(widget); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_RIGHT_MARGIN)) { edit->right_margin = value_int(v); edit_reset_layout(widget); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_TOP_MARGIN)) { edit->top_margin = value_int(v); edit_reset_layout(widget); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_BOTTOM_MARGIN)) { edit->bottom_margin = value_int(v); edit_reset_layout(widget); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_PASSWORD_VISIBLE)) { edit_set_password_visible(widget, value_bool(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_FOCUS) || tk_str_eq(name, WIDGET_PROP_FOCUSED)) { edit_set_focus(widget, value_bool(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_ACTION_TEXT)) { edit_set_action_text(widget, value_str(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_TIPS)) { edit_set_tips(widget, value_str(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_TR_TIPS)) { edit_set_tr_tips(widget, value_str(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_KEYBOARD)) { edit_set_keyboard(widget, value_str(v)); return RET_OK; } else if (tk_str_eq(name, WIDGET_PROP_VALUE) || tk_str_eq(name, WIDGET_PROP_TEXT)) { edit_set_text(widget, v); return RET_OK; } edit_update_status(widget); return RET_NOT_FOUND; } ret_t edit_set_password_visible(widget_t* widget, bool_t password_visible) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->password_visible = password_visible; text_edit_set_mask(edit->model, !password_visible); text_edit_set_mask_char(edit->model, PASSWORD_MASK_CHAR); text_edit_set_cursor(edit->model, 0xffffffff); widget_invalidate(widget, NULL); return RET_OK; } ret_t edit_set_focus(widget_t* widget, bool_t focus) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); widget_set_focused(widget, focus); edit_update_status(widget); return RET_OK; } ret_t edit_set_cursor(widget_t* widget, uint32_t cursor) { edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); return text_edit_set_cursor(edit->model, cursor); } uint32_t edit_get_cursor(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && edit->model != NULL, 0); return text_edit_get_cursor(edit->model); } static ret_t edit_add_float(edit_t* edit, double delta) { double v = 0; wstr_t* text = NULL; widget_t* widget = WIDGET(edit); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); text = &(widget->text); return_value_if_fail(wstr_to_float(text, &v) == RET_OK, RET_FAIL); v += delta; if (edit->min < edit->max) { if (v < edit->min) { wstr_from_float(text, edit->min); } else if (v > edit->max) { wstr_from_float(text, edit->max); } else { wstr_add_float(text, delta); } } else { wstr_add_float(text, delta); } wstr_trim_float_zero(text); return RET_OK; } static ret_t edit_add_int(edit_t* edit, int delta) { int32_t v = 0; wstr_t* text = NULL; widget_t* widget = WIDGET(edit); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); text = &(widget->text); return_value_if_fail(wstr_to_int(text, &v) == RET_OK, RET_FAIL); v += delta; if (edit->auto_fix && (edit->min < edit->max)) { if (v < edit->min) { v = (int32_t)(edit->min); } if (v > edit->max) { v = (int32_t)(edit->max); } } wstr_from_int(text, v); return RET_OK; } int32_t edit_get_int(widget_t* widget) { int32_t v = 0; return_value_if_fail(widget != NULL, 0); wstr_to_int(&(widget->text), &v); return v; } double edit_get_double(widget_t* widget) { double v = 0; return_value_if_fail(widget != NULL, 0); wstr_to_float(&(widget->text), &v); return v; } ret_t edit_set_int(widget_t* widget, int32_t value) { value_t v; return_value_if_fail(widget != NULL, RET_BAD_PARAMS); value_set_int32(&v, value); edit_set_text(widget, &v); return RET_OK; } ret_t edit_set_double(widget_t* widget, double value) { value_t v; return_value_if_fail(widget != NULL, RET_BAD_PARAMS); value_set_double(&v, value); return edit_set_text(widget, &v); } ret_t edit_set_double_ex(widget_t* widget, const char* format, double value) { value_t v; char text[64]; return_value_if_fail(widget != NULL, RET_BAD_PARAMS); if (format == NULL) { format = "%2.2lf"; } tk_snprintf(text, sizeof(text) - 1, format, value); value_set_str(&v, text); return edit_set_text(widget, &v); } static ret_t edit_inc_default(edit_t* edit) { wstr_t* text = NULL; widget_t* widget = WIDGET(edit); input_type_t input_type = (input_type_t)0; return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); text = &(widget->text); input_type = edit->input_type; wstr_set(&(edit->last_changing_text), text->str); switch (input_type) { case INPUT_FLOAT: case INPUT_UFLOAT: { float_t step = edit->step != 0.0 ? edit->step : 0.1; if (text->size == 0) { wstr_from_float(text, edit->min); wstr_trim_float_zero(text); } edit_add_float(edit, step); break; } case INPUT_INT: case INPUT_UINT: { int32_t step = edit->step != 0.0 ? edit->step : 1; if (text->size == 0) { wstr_from_int(text, edit->min); } edit_add_int(edit, step); break; } default: break; } if (!edit->readonly) { text_edit_select_all(edit->model); } text_edit_set_cursor(edit->model, text->size); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); return widget_invalidate_force(widget, NULL); } static ret_t edit_dec_default(edit_t* edit) { wstr_t* text = NULL; widget_t* widget = WIDGET(edit); input_type_t input_type = (input_type_t)0; return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); text = &(widget->text); input_type = edit->input_type; wstr_set(&(edit->last_changing_text), text->str); switch (input_type) { case INPUT_FLOAT: case INPUT_UFLOAT: { float_t step = edit->step != 0.0 ? edit->step : 0.1; if (text->size == 0) { wstr_from_float(text, edit->max); wstr_trim_float_zero(text); } edit_add_float(edit, -step); break; } case INPUT_INT: case INPUT_UINT: { int32_t step = edit->step != 0.0 ? edit->step : 1; if (text->size == 0) { wstr_from_int(text, edit->max); } edit_add_int(edit, -step); break; } default: break; } if (!edit->readonly) { text_edit_select_all(edit->model); } text_edit_set_cursor(edit->model, text->size); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); return widget_invalidate_force(widget, NULL); } ret_t edit_clear(edit_t* edit) { widget_t* widget = WIDGET(edit); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); wstr_set(&(widget->text), L""); text_edit_set_cursor(edit->model, 0xffffffff); edit_update_status(widget); if (edit->fix_value != NULL) { edit->fix_value(widget); } return widget_invalidate_force(widget, NULL); } static ret_t edit_on_inc(void* ctx, event_t* e) { (void)e; return edit_inc(EDIT(ctx)); } static ret_t edit_on_dec(void* ctx, event_t* e) { (void)e; return edit_dec(EDIT(ctx)); } static ret_t edit_on_clear(void* ctx, event_t* e) { (void)e; return edit_clear(EDIT(ctx)); } static ret_t edit_on_password_visible(void* ctx, event_t* e) { edit_t* edit = EDIT(ctx); bool_t password_visible = FALSE; widget_t* widget = WIDGET(e->target); return_value_if_fail(edit != NULL && widget != NULL, RET_BAD_PARAMS); password_visible = widget_get_prop_bool(widget, WIDGET_PROP_VALUE, edit->password_visible); edit_set_password_visible(WIDGET(edit), password_visible); return RET_OK; } static ret_t edit_hook_button(void* ctx, const void* iter) { widget_t* widget = WIDGET(iter); widget_t* edit = WIDGET(ctx); return_value_if_fail(widget != NULL && edit != NULL, RET_REMOVE); if (widget->name && widget != edit) { uint32_t etype = EVT_CLICK; event_func_t handler = NULL; const char* name = widget->name; emitter_t* emitter = widget->emitter; const char* type = widget_get_type(widget); if (tk_str_eq(type, WIDGET_TYPE_BUTTON)) { if (tk_str_eq(name, STR_EDIT_INC_NAME)) { handler = edit_on_inc; } else if (tk_str_eq(name, STR_EDIT_DEC_NAME)) { handler = edit_on_dec; } else if (tk_str_eq(name, STR_EDIT_CLEAR_NAME)) { handler = edit_on_clear; } } else if (tk_str_eq(type, WIDGET_TYPE_CHECK_BUTTON)) { if (tk_str_eq(name, STR_EDIT_VISIBLE_NAME)) { etype = EVT_VALUE_CHANGED; handler = edit_on_password_visible; } } if (handler != NULL && (emitter == NULL || !emitter_exist(emitter, etype, handler, edit))) { widget_on(widget, etype, handler, edit); } } return RET_OK; } ret_t edit_on_destroy(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(widget != NULL && edit != NULL, RET_BAD_PARAMS); if (edit->timer_id != TK_INVALID_ID) { timer_remove(edit->timer_id); edit->timer_id = TK_INVALID_ID; } if (edit->idle_id != TK_INVALID_ID) { idle_remove(edit->idle_id); edit->idle_id = TK_INVALID_ID; } TKMEM_FREE(edit->tips); TKMEM_FREE(edit->tr_tips); TKMEM_FREE(edit->keyboard); TKMEM_FREE(edit->action_text); wstr_reset(&(edit->saved_text)); wstr_reset(&(edit->last_changing_text)); wstr_reset(&(edit->last_changed_text)); text_edit_destroy(edit->model); return RET_OK; } static ret_t edit_hook_children_button_and_check_valid_value(const idle_info_t* info) { widget_t* widget = WIDGET(info->ctx); edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_REMOVE); widget_foreach(widget, edit_hook_button, widget); edit->idle_id = TK_INVALID_ID; edit_check_valid_value(widget); return RET_REMOVE; } ret_t edit_on_add_child(widget_t* widget, widget_t* child) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_FAIL); if (edit->idle_id == TK_INVALID_ID) { edit->idle_id = widget_add_idle(widget, edit_hook_children_button_and_check_valid_value); } return RET_CONTINUE; } const char* const s_edit_properties[] = {TK_EDIT_PROPS, NULL}; ret_t edit_on_copy(widget_t* widget, widget_t* other) { edit_t* edit = EDIT(widget); edit_t* edit_other = EDIT(other); return_value_if_fail(edit != NULL && edit_other != NULL, RET_BAD_PARAMS); edit->min = edit_other->min; edit->max = edit_other->max; edit->step = edit_other->step; edit->readonly = edit_other->readonly; edit->auto_fix = edit_other->auto_fix; edit->left_margin = edit_other->left_margin; edit->right_margin = edit_other->right_margin; edit->top_margin = edit_other->top_margin; edit->bottom_margin = edit_other->bottom_margin; edit->password_visible = edit_other->password_visible; edit_set_input_type(widget, edit_other->input_type); if (edit_other->tips != NULL) { edit_set_tips(widget, edit_other->tips); } if (edit_other->tr_tips != NULL) { edit_set_tr_tips(widget, edit_other->tr_tips); } return RET_OK; } TK_DECL_VTABLE(edit) = {.size = sizeof(edit_t), .type = WIDGET_TYPE_EDIT, .focusable = TRUE, .inputable = TRUE, .pointer_cursor = WIDGET_CURSOR_EDIT, .clone_properties = s_edit_properties, .persistent_properties = s_edit_properties, .get_parent_vt = TK_GET_PARENT_VTABLE(widget), .create = edit_create, .on_re_translate = edit_on_re_translate, .on_paint_self = edit_on_paint_self, .set_prop = edit_set_prop, .get_prop = edit_get_prop, .on_destroy = edit_on_destroy, .on_copy = edit_on_copy, .on_event = edit_on_event, .on_add_child = edit_on_add_child}; widget_t* edit_create_ex(widget_t* parent, const widget_vtable_t* vt, xy_t x, xy_t y, wh_t w, wh_t h) { widget_t* widget = widget_create(parent, vt, x, y, w, h); edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, NULL); edit->margin = 2; edit->top_margin = 0; edit->left_margin = 0; edit->right_margin = 0; edit->bottom_margin = 0; edit->close_im_when_blured = TRUE; edit->open_im_when_focused = TRUE; edit_set_text_limit(widget, 0, 1024); edit_update_status(widget); edit->timer_id = TK_INVALID_ID; edit->model = text_edit_create(widget, TRUE); ENSURE(edit->model != NULL); text_edit_set_on_text_will_delete(edit->model, edit_on_text_edit_text_will_delete, widget); text_edit_set_on_char_will_input(edit->model, edit_on_text_edit_char_will_input, widget); wstr_init(&(edit->last_changing_text), 0); wstr_init(&(edit->last_changed_text), 0); widget_set_text(widget, L""); wstr_init(&(edit->saved_text), 0); edit_set_password_visible(widget, FALSE); edit_set_action_text(widget, ACTION_TEXT_DONE); return widget; } widget_t* edit_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) { return edit_create_ex(parent, TK_REF_VTABLE(edit), x, y, w, h); } widget_t* edit_cast(widget_t* widget) { return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, edit), NULL); return widget; } ret_t edit_set_is_valid_char(widget_t* widget, edit_is_valid_char_t is_valid_char) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->is_valid_char = is_valid_char; return RET_OK; } ret_t edit_set_is_valid_value(widget_t* widget, edit_is_valid_value_t is_valid_value) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->is_valid_value = is_valid_value; return RET_OK; } ret_t edit_set_fix_value(widget_t* widget, edit_fix_value_t fix_value) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->fix_value = fix_value; return RET_OK; } ret_t edit_set_inc_value(widget_t* widget, edit_inc_value_t inc_value) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->inc_value = inc_value; return RET_OK; } ret_t edit_set_dec_value(widget_t* widget, edit_dec_value_t dec_value) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->dec_value = dec_value; return RET_OK; } ret_t edit_set_pre_input(widget_t* widget, edit_pre_input_t pre_input) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->pre_input = pre_input; return RET_OK; } ret_t edit_set_pre_delete(widget_t* widget, edit_pre_delete_t pre_delete) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); edit->pre_delete = pre_delete; return RET_OK; } ret_t edit_set_select(widget_t* widget, uint32_t start, uint32_t end) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && edit->model != NULL, RET_BAD_PARAMS); return text_edit_set_select(edit->model, start, end); } char* edit_get_selected_text(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && edit->model != NULL, NULL); return text_edit_get_selected_text(edit->model); } ret_t edit_pre_input_with_sep(widget_t* widget, uint32_t key, char sep) { text_edit_state_t state; edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && widget != NULL, RET_BAD_PARAMS); if (key == TK_KEY_LEFT || key == TK_KEY_RIGHT) { wstr_t* text = &(widget->text); text_edit_get_state(edit->model, &state); if (state.select_start == state.select_end && text->size > 1) { int32_t i = 0; if (key == TK_KEY_LEFT) { if (state.cursor > 1 && text->str[state.cursor - 1] == sep) { /*select prev part*/ for (i = state.cursor - 2; i >= 0; i--) { if (text->str[i] == sep) { break; } } text_edit_set_cursor(edit->model, state.cursor - 1); text_edit_set_select(edit->model, i + 1, state.cursor - 1); return RET_STOP; } } else if (key == TK_KEY_RIGHT) { if (text->str[state.cursor] == sep) { /*select next part*/ for (i = state.cursor + 1; i < text->size; i++) { if (text->str[i] == sep) { break; } } text_edit_set_cursor(edit->model, state.cursor + 1); text_edit_set_select(edit->model, state.cursor + 1, i); return RET_STOP; } } } } return RET_OK; } ret_t edit_pre_delete_with_sep(widget_t* widget, delete_type_t delete_type, char sep) { text_edit_state_t state; wstr_t* text = NULL; edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && widget != NULL, RET_BAD_PARAMS); text = &(widget->text); text_edit_get_state(edit->model, &state); if (state.select_start < state.select_end) { uint32_t i = 0; wchar_t* s = text->str + state.select_start; wchar_t* d = text->str + state.select_start; for (i = state.select_start; i < state.select_end; i++, s++) { if (*s == sep) { *d++ = sep; } } for (; i < text->size; i++) { *d++ = *s++; } text->size = d - text->str; *d = 0; text_edit_unselect(edit->model); text_edit_set_cursor(edit->model, state.select_start); if (delete_type == DELETE_BY_KEY_BACKSPACE || delete_type == DELETE_BY_KEY_DELETE) { return RET_STOP; } } else { if (delete_type == DELETE_BY_KEY_BACKSPACE) { if (state.cursor > 0 && text->str[state.cursor - 1] == sep) { text_edit_set_cursor(edit->model, state.cursor - 1); return RET_STOP; } } else if (delete_type == DELETE_BY_KEY_DELETE) { if (text->str[state.cursor] == sep) { text_edit_set_cursor(edit->model, state.cursor + 1); return RET_STOP; } } } return RET_OK; } ret_t edit_add_value_with_sep(widget_t* widget, int delta, char sep) { char c = 0; uint32_t cursor = 0; wstr_t* text = NULL; text_edit_state_t state; edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL && widget != NULL, RET_BAD_PARAMS); text = &(widget->text); text_edit_get_state(edit->model, &state); if (text->size == 0) { edit->fix_value(widget); return RET_OK; } wstr_set(&(edit->last_changing_text), text->str); cursor = state.cursor < text->size ? state.cursor : text->size - 1; if (text->str[cursor] == sep && cursor > 0) { cursor--; } c = text->str[cursor]; if (c >= '0' && c <= '9') { c += delta; if (c < '0') { c = '9'; } if (c > '9') { c = '0'; } text->str[cursor] = c; } text_edit_set_select(edit->model, cursor, cursor + 1); edit_dispatch_value_change_event(widget, EVT_VALUE_CHANGING); return widget_invalidate_force(widget, NULL); } ret_t edit_inc(edit_t* edit) { if (edit->inc_value != NULL) { return edit->inc_value(WIDGET(edit)); } else { return edit_inc_default(edit); } } ret_t edit_dec(edit_t* edit) { if (edit->dec_value != NULL) { return edit->dec_value(WIDGET(edit)); } else { return edit_dec_default(edit); } } static ret_t edit_auto_fix(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->fix_value != NULL) { return edit->fix_value(widget); } else { return edit_auto_fix_default(widget); } } bool_t edit_is_valid_value(widget_t* widget) { edit_t* edit = EDIT(widget); return_value_if_fail(edit != NULL, RET_BAD_PARAMS); if (edit->is_valid_value != NULL) { return edit->is_valid_value(widget); } else { return edit_is_valid_value_default(widget); } }