Files
luban-lite-t3e-pro/packages/third-party/awtk-ui/awtk/src/widgets/edit.c
刘可亮 3b4064f334 v1.0.2
2023-11-30 19:48:02 +08:00

2211 lines
60 KiB
C

/**
* 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 <xianjimli@hotmail.com> created
* 2018-11-20 Li YaoShen <liyaoshen@zlg.cn> 1.text selection; 2.cursor movable
* 2018-06-24 Li XianJing <xianjimli@hotmail.com> 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);
}
}