Files
MAX_CARLINK_A270S/MXC_A27-PCB4.5-270T/lib/awtk/awtk/src/ext_widgets/mledit/mledit.c
2025-01-21 16:49:37 +08:00

1130 lines
35 KiB
C

/**
* File: mledit.c
* Author: AWTK Develop Team
* Brief: mledit
*
* Copyright (c) 2018 - 2021 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:
* ================================================================
* 2019-06-08 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "tkc/mem.h"
#include "tkc/utf8.h"
#include "tkc/utils.h"
#include "tkc/time_now.h"
#include "base/events.h"
#include "mledit/mledit.h"
#include "mledit/line_number.h"
#include "base/input_method.h"
#include "scroll_view/scroll_bar.h"
static ret_t mledit_update_status(widget_t* widget);
static ret_t mledit_save_text(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->cancelable) {
wstr_set(&(mledit->saved_text), widget->text.str);
}
return RET_OK;
}
static ret_t mledit_rollback_text(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->cancelable) {
widget_set_text(widget, mledit->saved_text.str);
}
return RET_OK;
}
static ret_t mledit_commit_text(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->cancelable) {
wstr_set(&(mledit->saved_text), widget->text.str);
mledit_update_status(widget);
}
return RET_OK;
}
static ret_t mledit_dispatch_event(widget_t* widget, event_type_t type) {
value_change_event_t evt;
value_change_event_init(&evt, type, widget);
value_set_wstr(&(evt.old_value), widget->text.str);
value_set_wstr(&(evt.new_value), widget->text.str);
widget_dispatch(widget, (event_t*)&evt);
return RET_OK;
}
ret_t mledit_set_tips(widget_t* widget, const char* tips) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && tips != NULL, RET_BAD_PARAMS);
mledit->tips = tk_str_copy(mledit->tips, tips);
text_edit_set_tips(mledit->model, mledit->tips, TRUE);
return RET_OK;
}
static ret_t mledit_apply_tr_text_before_paint(void* ctx, event_t* e) {
widget_t* widget = WIDGET(ctx);
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->tr_tips != NULL) {
const char* tr_tips = locale_info_tr(widget_get_locale_info(widget), mledit->tr_tips);
mledit_set_tips(widget, tr_tips);
}
return RET_REMOVE;
}
ret_t mledit_set_tr_tips(widget_t* widget, const char* tr_tips) {
mledit_t* mledit = MLEDIT(widget);
widget_t* win = widget_get_window(widget);
return_value_if_fail(mledit != NULL && tr_tips != NULL, RET_BAD_PARAMS);
mledit->tr_tips = tk_str_copy(mledit->tr_tips, tr_tips);
if (win != NULL) {
tr_tips = locale_info_tr(widget_get_locale_info(widget), tr_tips);
mledit_set_tips(widget, tr_tips);
} else {
widget_on(widget, EVT_BEFORE_PAINT, mledit_apply_tr_text_before_paint, widget);
}
return RET_OK;
}
ret_t mledit_set_keyboard(widget_t* widget, const char* keyboard) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && keyboard != NULL, RET_BAD_PARAMS);
mledit->keyboard = tk_str_copy(mledit->keyboard, keyboard);
return RET_OK;
}
ret_t mledit_set_focus(widget_t* widget, bool_t focus) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
widget_set_focused(widget, focus);
mledit_update_status(widget);
return RET_OK;
}
ret_t mledit_set_readonly(widget_t* widget, bool_t readonly) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->readonly = readonly;
return RET_OK;
}
ret_t mledit_set_cancelable(widget_t* widget, bool_t cancelable) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->cancelable = cancelable;
return RET_OK;
}
ret_t mledit_set_wrap_word(widget_t* widget, bool_t wrap_word) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->wrap_word = wrap_word;
text_edit_set_wrap_word(mledit->model, wrap_word);
return RET_OK;
}
ret_t mledit_set_overwrite(widget_t* widget, bool_t overwrite) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->overwrite = overwrite;
text_edit_set_overwrite(mledit->model, overwrite);
return RET_OK;
}
ret_t mledit_set_max_lines(widget_t* widget, uint32_t max_lines) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->max_lines = max_lines;
text_edit_set_max_rows(mledit->model, max_lines);
text_edit_layout(mledit->model);
return RET_OK;
}
ret_t mledit_set_max_chars(widget_t* widget, uint32_t max_chars) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->max_chars = max_chars;
text_edit_set_max_chars(mledit->model, max_chars);
text_edit_layout(mledit->model);
return RET_OK;
}
static ret_t mledit_get_prop(widget_t* widget, const char* name, value_t* v) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_READONLY)) {
value_set_bool(v, mledit->readonly);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CANCELABLE)) {
value_set_bool(v, mledit->cancelable);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_WRAP_WORD)) {
value_set_bool(v, mledit->wrap_word);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_OVERWRITE)) {
value_set_bool(v, mledit->overwrite);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_MAX_LINES)) {
value_set_int(v, mledit->max_lines);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_MAX_CHARS)) {
value_set_int(v, mledit->max_chars);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_SCROLL_LINE)) {
value_set_int(v, mledit->scroll_line);
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 = mledit->left_margin != 0 ? mledit->left_margin : mledit->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 = mledit->right_margin != 0 ? mledit->right_margin : mledit->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 = mledit->top_margin != 0 ? mledit->top_margin : mledit->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 = mledit->bottom_margin != 0 ? mledit->bottom_margin : mledit->margin;
}
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TIPS)) {
value_set_str(v, mledit->tips);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TR_TIPS)) {
value_set_str(v, mledit->tr_tips);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_KEYBOARD)) {
value_set_str(v, mledit->keyboard);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_FOCUSABLE)) {
value_set_bool(v, !(mledit->readonly));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
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(mledit->model, &state);
value_set_int(v, state.caret.x - state.ox);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CARET_Y)) {
text_edit_state_t state;
text_edit_get_state(mledit->model, &state);
value_set_int(v, state.caret.y - state.oy);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_OPEN_IM_WHEN_FOCUSED)) {
value_set_bool(v, mledit->open_im_when_focused);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CLOSE_IM_WHEN_BLURED)) {
value_set_bool(v, mledit->close_im_when_blured);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_INPUTING)) {
int64_t delta = (time_now_ms() - mledit->last_user_action_time);
bool_t inputing =
(delta < TK_INPUTING_TIMEOUT) && (mledit->last_user_action_time > 0) && widget->focused;
value_set_bool(v, inputing);
return RET_OK;
}
return RET_NOT_FOUND;
}
static void mledit_reset_text_edit_layout(text_edit_t* text_edit) {
text_edit_layout(text_edit);
text_edit_set_offset(text_edit, 0, 0);
text_edit_set_select(text_edit, 0, 0);
text_edit_set_cursor(text_edit, text_edit_get_cursor(text_edit));
}
static ret_t mledit_set_text(widget_t* widget, const value_t* v) {
wstr_t str;
wstr_init(&str, 0);
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(wstr_from_value(&str, v) == RET_OK, RET_BAD_PARAMS);
if (!wstr_equal(&(widget->text), &str)) {
wstr_set(&(widget->text), str.str);
mledit_reset_text_edit_layout(mledit->model);
text_edit_layout(mledit->model);
mledit_dispatch_event(widget, EVT_VALUE_CHANGED);
mledit_update_status(widget);
}
wstr_reset(&str);
return RET_OK;
}
static ret_t mledit_set_prop(widget_t* widget, const char* name, const value_t* v) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && name != NULL && v != NULL, RET_BAD_PARAMS);
if (tk_str_eq(name, WIDGET_PROP_TEXT) || tk_str_eq(name, WIDGET_PROP_VALUE)) {
mledit_set_text(widget, v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_READONLY)) {
mledit->readonly = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CANCELABLE)) {
mledit->cancelable = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_WRAP_WORD)) {
mledit_set_wrap_word(widget, value_bool(v));
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_OVERWRITE)) {
mledit_set_overwrite(widget, value_bool(v));
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_MAX_LINES)) {
mledit_set_max_lines(widget, value_int(v));
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_MAX_CHARS)) {
mledit_set_max_chars(widget, value_int(v));
return RET_OK;
} else if (tk_str_eq(name, MLEDIT_PROP_SCROLL_LINE)) {
mledit_set_scroll_line(widget, value_int(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_OPEN_IM_WHEN_FOCUSED)) {
mledit->open_im_when_focused = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_CLOSE_IM_WHEN_BLURED)) {
mledit->close_im_when_blured = value_bool(v);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_MARGIN)) {
mledit->margin = value_int(v);
mledit_reset_text_edit_layout(mledit->model);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_LEFT_MARGIN)) {
mledit->left_margin = value_int(v);
mledit_reset_text_edit_layout(mledit->model);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_RIGHT_MARGIN)) {
mledit->right_margin = value_int(v);
mledit_reset_text_edit_layout(mledit->model);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TOP_MARGIN)) {
mledit->top_margin = value_int(v);
mledit_reset_text_edit_layout(mledit->model);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_BOTTOM_MARGIN)) {
mledit->bottom_margin = value_int(v);
mledit_reset_text_edit_layout(mledit->model);
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_FOCUS) || tk_str_eq(name, WIDGET_PROP_FOCUSED)) {
mledit_set_focus(widget, value_bool(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TIPS)) {
mledit_set_tips(widget, value_str(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TR_TIPS)) {
mledit_set_tr_tips(widget, value_str(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_KEYBOARD)) {
mledit_set_keyboard(widget, value_str(v));
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_TEXT)) {
return RET_OK;
} else if (tk_str_eq(name, WIDGET_PROP_VALUE)) {
wstr_from_value(&(widget->text), v);
return RET_OK;
}
return RET_NOT_FOUND;
}
static ret_t mledit_on_destroy(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && mledit != NULL, RET_BAD_PARAMS);
if (mledit->timer_id != TK_INVALID_ID) {
timer_remove(mledit->timer_id);
}
wstr_reset(&(mledit->temp));
TKMEM_FREE(mledit->tips);
TKMEM_FREE(mledit->tr_tips);
TKMEM_FREE(mledit->keyboard);
wstr_reset(&(mledit->saved_text));
text_edit_destroy(mledit->model);
return RET_OK;
}
static ret_t mledit_on_paint_self(widget_t* widget, canvas_t* c) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
text_edit_paint(mledit->model, c);
return RET_OK;
}
static ret_t mledit_commit_str(widget_t* widget, const char* str) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
wstr_set_utf8(&(mledit->temp), str);
text_edit_paste(mledit->model, mledit->temp.str, mledit->temp.size);
mledit_dispatch_event(widget, EVT_VALUE_CHANGING);
return RET_OK;
}
static ret_t mledit_request_input_method_on_window_open(void* ctx, event_t* e) {
mledit_t* mledit = MLEDIT(ctx);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (!mledit->readonly) {
input_method_request(input_method(), WIDGET(ctx));
}
return RET_REMOVE;
}
static ret_t mledit_request_input_method(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->readonly) {
return RET_OK;
}
if (widget_is_window_opened(widget)) {
input_method_request(input_method(), widget);
} else {
widget_t* win = widget_get_window(widget);
if (win != NULL) {
widget_on(win, EVT_WINDOW_OPEN, mledit_request_input_method_on_window_open, widget);
}
}
return RET_OK;
}
static ret_t mledit_update_caret(const timer_info_t* timer) {
mledit_t* mledit = NULL;
widget_t* widget = NULL;
return_value_if_fail(timer != NULL, RET_REMOVE);
mledit = MLEDIT(timer->ctx);
widget = WIDGET(timer->ctx);
return_value_if_fail(mledit != NULL && widget != NULL, RET_REMOVE);
if (mledit->readonly) {
return RET_REMOVE;
}
widget_invalidate_force(widget, NULL);
text_edit_invert_caret_visible(mledit->model);
if (widget->focused) {
return RET_REPEAT;
} else {
mledit->timer_id = TK_INVALID_ID;
text_edit_set_caret_visible(mledit->model, FALSE);
return RET_REMOVE;
}
}
static ret_t mledit_update_status(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && widget != NULL, RET_BAD_PARAMS);
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 (mledit->cancelable) {
if (!wstr_equal(&(mledit->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 {
widget_set_state(widget, WIDGET_STATE_NORMAL);
}
}
return RET_OK;
}
static ret_t mledit_pointer_up_cleanup(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && widget != NULL, RET_BAD_PARAMS);
widget_ungrab(widget->parent, widget);
widget_set_state(widget, WIDGET_STATE_NORMAL);
return RET_OK;
}
ret_t mledit_clear(mledit_t* mledit) {
widget_t* widget = WIDGET(mledit);
return_value_if_fail(widget != NULL && mledit != NULL, RET_BAD_PARAMS);
widget->text.size = 0;
text_edit_set_cursor(mledit->model, 0);
return widget_invalidate_force(widget, NULL);
}
ret_t mledit_set_cursor(widget_t* widget, uint32_t cursor) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && mledit != NULL, RET_BAD_PARAMS);
return text_edit_set_cursor(mledit->model, cursor);
}
uint32_t mledit_get_cursor(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && mledit != NULL, 0);
return text_edit_get_cursor(mledit->model);
}
ret_t mledit_set_scroll_line(widget_t* widget, uint32_t scroll_line) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && mledit != NULL, RET_BAD_PARAMS);
mledit->scroll_line = scroll_line;
return RET_OK;
}
ret_t mledit_scroll_to_offset(widget_t* widget, uint32_t offset) {
mledit_t* mledit = MLEDIT(widget);
int32_t scroll_y = 0;
scroll_bar_t* vscroll_bar =
SCROLL_BAR(widget_lookup_by_type(widget, WIDGET_TYPE_SCROLL_BAR_DESKTOP, TRUE));
return_value_if_fail(mledit != NULL && mledit->model != NULL, RET_BAD_PARAMS);
scroll_y = text_edit_get_height(mledit->model, offset);
if (vscroll_bar != NULL) {
text_edit_state_t state = {0};
text_edit_get_state(mledit->model, &state);
scroll_y = tk_min(scroll_y, tk_max(vscroll_bar->virtual_size - vscroll_bar->widget.h, 0));
}
return text_edit_set_offset(mledit->model, 0, scroll_y);
}
static ret_t mledit_focus_set_cursor(const idle_info_t* info) {
mledit_t* mledit = MLEDIT(info->ctx);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit_set_cursor(WIDGET(mledit), text_edit_get_cursor(mledit->model));
return RET_REMOVE;
}
static ret_t mledit_on_event(widget_t* widget, event_t* e) {
ret_t ret = RET_OK;
uint32_t type = e->type;
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(widget != NULL && mledit != NULL && e != 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(mledit->model, evt.x, evt.y);
widget_grab(widget->parent, widget);
}
if (widget->target == NULL) {
mledit_request_input_method(widget);
}
mledit->last_user_action_time = e->time;
mledit_update_status(widget);
widget_invalidate(widget, NULL);
break;
}
case EVT_POINTER_DOWN_ABORT: {
mledit_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) {
if (widget->target == NULL) {
text_edit_set_lock_scrollbar_value(mledit->model, FALSE);
text_edit_drag(mledit->model, evt.x, evt.y);
text_edit_set_lock_scrollbar_value(mledit->model, mledit->lock_scrollbar_value);
ret = RET_STOP;
}
}
if (evt.pressed) {
mledit->last_user_action_time = e->time;
}
widget_invalidate(widget, NULL);
break;
}
case EVT_POINTER_UP: {
widget_ungrab(widget->parent, widget);
mledit->last_user_action_time = e->time;
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 (key == TK_KEY_ESCAPE || (key >= TK_KEY_F1 && key <= TK_KEY_F12)) {
break;
}
if (mledit->readonly) {
if (is_control && (key == TK_KEY_C || key == TK_KEY_c)) {
log_debug("copy\n");
} else {
break;
}
}
text_edit_key_down(mledit->model, (key_event_t*)e);
if ((key < 128 && tk_isprint(key)) || key == TK_KEY_BACKSPACE || key == TK_KEY_DELETE ||
key == TK_KEY_TAB || key_code_is_enter(key)) {
mledit_dispatch_event(widget, EVT_VALUE_CHANGING);
}
mledit_update_status(widget);
ret = RET_STOP;
mledit->last_user_action_time = e->time;
widget_invalidate(widget, NULL);
break;
}
case EVT_IM_COMMIT: {
text_edit_state_t state;
text_edit_get_state(mledit->model, &state);
im_commit_event_t* evt = (im_commit_event_t*)e;
if (mledit->readonly) {
break;
}
if (state.preedit) {
text_edit_preedit_clear(mledit->model);
}
if (evt->replace) {
mledit_clear(mledit);
}
mledit_commit_str(widget, evt->text);
mledit_update_status(widget);
mledit->last_user_action_time = e->time;
widget_invalidate(widget, NULL);
break;
}
case EVT_IM_PREEDIT: {
mledit->last_user_action_time = e->time;
text_edit_preedit(mledit->model);
break;
}
case EVT_IM_PREEDIT_CONFIRM: {
mledit->last_user_action_time = e->time;
text_edit_preedit_confirm(mledit->model);
break;
}
case EVT_IM_PREEDIT_ABORT: {
mledit->last_user_action_time = e->time;
text_edit_preedit_abort(mledit->model);
break;
}
case EVT_IM_ACTION: {
mledit_commit_str(widget, "\n");
widget_invalidate(widget, NULL);
break;
}
case EVT_KEY_UP: {
key_event_t* evt = key_event_cast(e);
int32_t key = evt->key;
if (key == TK_KEY_ESCAPE || (key >= TK_KEY_F1 && key <= TK_KEY_F12)) {
break;
}
if (key_code_is_enter(key)) {
ret = RET_STOP;
} else {
ret = text_edit_key_up(mledit->model, evt);
}
mledit->last_user_action_time = e->time;
widget_invalidate(widget, NULL);
break;
}
case EVT_IM_CANCEL: {
mledit_rollback_text(widget);
break;
}
case EVT_BLUR: {
if (mledit->close_im_when_blured) {
input_method_request(input_method(), NULL);
}
text_edit_preedit_confirm(mledit->model);
mledit_update_status(widget);
mledit_dispatch_event(widget, EVT_VALUE_CHANGED);
mledit_commit_text(widget);
break;
}
case EVT_FOCUS: {
if (mledit->timer_id == TK_INVALID_ID) {
mledit->timer_id = timer_add(mledit_update_caret, widget, 600);
}
if (widget->target == NULL) {
if (mledit->open_im_when_focused) {
mledit_request_input_method(widget);
}
widget_add_idle(widget, mledit_focus_set_cursor);
}
mledit_save_text(widget);
break;
}
case EVT_WHEEL: {
key_event_t kevt;
wheel_event_t* evt = (wheel_event_t*)e;
int32_t delta = evt->dy;
widget_t* vscroll_bar = widget_lookup_by_type(widget, WIDGET_TYPE_SCROLL_BAR_DESKTOP, TRUE);
if (vscroll_bar != NULL) {
int32_t font_size = style_get_int(widget->astyle, STYLE_ID_FONT_SIZE, TK_DEFAULT_FONT_SIZE);
if (delta > 0) {
scroll_bar_add_delta(vscroll_bar, -font_size * mledit->scroll_line);
} else if (delta < 0) {
scroll_bar_add_delta(vscroll_bar, font_size * mledit->scroll_line);
}
} else {
if (delta > 0) {
key_event_init(&kevt, EVT_KEY_DOWN, widget, TK_KEY_UP);
text_edit_key_down(mledit->model, (key_event_t*)&kevt);
} else if (delta < 0) {
key_event_init(&kevt, EVT_KEY_DOWN, widget, TK_KEY_DOWN);
text_edit_key_down(mledit->model, (key_event_t*)&kevt);
}
}
ret = RET_STOP;
mledit->last_user_action_time = e->time;
widget_invalidate(widget, NULL);
break;
}
case EVT_RESIZE:
case EVT_MOVE_RESIZE: {
mledit_reset_text_edit_layout(mledit->model);
widget_invalidate(widget, NULL);
break;
}
case EVT_VALUE_CHANGING: {
mledit_update_status(widget);
widget_invalidate(widget, NULL);
break;
}
case EVT_WIDGET_LOAD: {
uint32_t max_size = widget->text.size;
uint32_t line_break_num = mledit->max_lines;
uint32_t i = 0;
for (i = 0; i < max_size; i++) {
if (i + 1 < max_size &&
TWINS_WCHAR_IS_LINE_BREAK(widget->text.str[i], widget->text.str[i + 1])) {
line_break_num--;
i++;
} else if (WCHAR_IS_LINE_BREAK(widget->text.str[i])) {
line_break_num--;
}
if (line_break_num == 0) {
max_size = i;
break;
}
}
if (mledit->max_chars != 0) {
max_size = tk_min(max_size, mledit->max_chars);
}
if (max_size != widget->text.size) {
char* text = TKMEM_ZALLOCN(char, widget->text.size);
wstr_get_utf8(&widget->text, text, widget->text.size + 1);
text[max_size] = '\0';
widget_set_text_utf8(widget, text);
TKMEM_FREE(text);
}
}
default:
break;
}
return ret;
}
static ret_t mledit_on_re_translate(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (mledit->tr_tips != NULL) {
const char* tr_tips = locale_info_tr(widget_get_locale_info(widget), mledit->tr_tips);
mledit_set_tips(widget, tr_tips);
}
return RET_OK;
}
static ret_t mledit_sync_line_number(widget_t* widget, text_edit_state_t* state) {
mledit_t* mledit = MLEDIT(widget);
widget_t* line_number = widget_lookup_by_type(widget, WIDGET_TYPE_LINE_NUMBER, TRUE);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
if (line_number != NULL) {
const uint32_t* lines_of_each_row = text_edit_get_lines_of_each_row(mledit->model);
if (lines_of_each_row != NULL) {
line_number_set_lines_of_each_row(line_number, lines_of_each_row, state->max_rows);
}
line_number_set_yoffset(line_number, state->oy);
line_number_set_line_height(line_number, state->line_height);
line_number_set_top_margin(line_number, mledit->top_margin);
line_number_set_bottom_margin(line_number, mledit->bottom_margin);
widget_invalidate_force(line_number, NULL);
}
return RET_BAD_PARAMS;
}
static ret_t mledit_sync_scrollbar(widget_t* widget, text_edit_state_t* state) {
xy_t y = 0;
wh_t virtual_h = 0;
int32_t margin = 0;
int32_t margin_top = 0;
int32_t margin_bottom = 0;
widget_t* vscroll_bar = NULL;
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
margin = style_get_int(widget->astyle, STYLE_ID_MARGIN, 0);
margin_top = style_get_int(widget->astyle, STYLE_ID_MARGIN_TOP, margin);
margin_bottom = style_get_int(widget->astyle, STYLE_ID_MARGIN_BOTTOM, margin);
virtual_h = (state->last_line_number + 1) * state->line_height + margin_top + margin_bottom;
vscroll_bar = widget_lookup_by_type(widget, WIDGET_TYPE_SCROLL_BAR_DESKTOP, TRUE);
if (vscroll_bar != NULL) {
virtual_h = virtual_h >= vscroll_bar->h ? virtual_h : vscroll_bar->h;
if (virtual_h > vscroll_bar->h) {
y = state->oy * virtual_h / (virtual_h - vscroll_bar->h);
} else {
y = 0;
}
scroll_bar_set_params(vscroll_bar, virtual_h, state->line_height);
scroll_bar_set_value_only(vscroll_bar, y);
widget_invalidate_force(vscroll_bar, NULL);
}
return RET_OK;
}
static ret_t mledit_on_text_edit_state_changed(void* ctx, text_edit_state_t* state) {
widget_t* widget = WIDGET(ctx);
mledit_sync_line_number(widget, state);
mledit_sync_scrollbar(widget, state);
widget_invalidate_force(widget, NULL);
return RET_OK;
}
static ret_t mledit_on_scroll_bar_value_changed(void* ctx, event_t* e) {
int32_t value = 0;
mledit_t* mledit = MLEDIT(ctx);
widget_t* vscroll_bar = e != NULL ? WIDGET(e->target) : NULL;
scroll_bar_t* scroll_bar = SCROLL_BAR(vscroll_bar);
return_value_if_fail(vscroll_bar != NULL && scroll_bar != NULL, RET_BAD_PARAMS);
value = widget_get_value(vscroll_bar);
value = (scroll_bar->virtual_size - vscroll_bar->h) * value / scroll_bar->virtual_size;
if (mledit->overwrite && mledit->max_chars == 0 && mledit->max_lines != 0) {
if (value == scroll_bar->virtual_size - vscroll_bar->h) {
mledit->lock_scrollbar_value = FALSE;
} else {
mledit->lock_scrollbar_value = TRUE;
}
text_edit_set_lock_scrollbar_value(mledit->model, mledit->lock_scrollbar_value);
}
text_edit_set_offset(mledit->model, 0, value);
return RET_OK;
}
ret_t mledit_set_open_im_when_focused(widget_t* widget, bool_t open_im_when_focused) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->open_im_when_focused = open_im_when_focused;
return RET_OK;
}
ret_t mledit_set_close_im_when_blured(widget_t* widget, bool_t close_im_when_blured) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit->close_im_when_blured = close_im_when_blured;
return RET_OK;
}
ret_t mledit_set_select(widget_t* widget, uint32_t start, uint32_t end) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && mledit->model != NULL, RET_BAD_PARAMS);
return text_edit_set_select(mledit->model, start, end);
}
char* mledit_get_selected_text(widget_t* widget) {
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && mledit->model != NULL, NULL);
return text_edit_get_selected_text(mledit->model);
}
static slist_t* mledit_get_rows_by_text(widget_t* widget, slist_t* slist, const char* text) {
uint32_t text_size = 0;
uint32_t i = 0;
return_value_if_fail(widget != NULL && slist != NULL && text != NULL, NULL);
text_size = tk_strlen(text);
slist_remove_all(slist);
for (i = 0; i < text_size; i++) {
if (i + 1 < text_size && TWINS_CHAR_IS_LINE_BREAK(text[i], text[i + 1])) {
i++;
slist_append(slist, tk_pointer_from_int((int32_t)(i + 1)));
} else if (CHAR_IS_LINE_BREAK(text[i])) {
slist_append(slist, tk_pointer_from_int((int32_t)(i + 1)));
}
}
return slist;
}
ret_t mledit_insert_text(widget_t* widget, uint32_t offset, const char* text) {
ret_t ret = RET_OK;
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL && mledit->model != NULL, RET_BAD_PARAMS);
if (!mledit->overwrite || mledit->max_chars > 0 || mledit->max_lines == 0) {
ret = text_edit_insert_text(mledit->model, offset, text);
} else {
uint32_t rows_start_offset = 0;
slist_t offset_list;
slist_init(&offset_list, NULL, NULL);
if (mledit_get_rows_by_text(widget, &offset_list, text) != NULL) {
uint32_t text_size = tk_strlen(text);
if (mledit->max_lines == 1) {
rows_start_offset = (uint32_t)tk_pointer_to_int(slist_tail_pop(&offset_list));
if (rows_start_offset < text_size) {
wstr_reset(&widget->text);
ret = text_edit_insert_text(mledit->model, offset, text + rows_start_offset);
}
} else {
slist_node_t* iter = offset_list.first;
while (ret == RET_OK && iter != NULL) {
uint32_t rows_end_offset = (uint32_t)tk_pointer_to_int(iter->data);
ret = text_edit_overwrite_text(mledit->model, &offset, text + rows_start_offset,
rows_end_offset - rows_start_offset);
rows_start_offset = rows_end_offset;
iter = iter->next;
}
if (ret == RET_OK && rows_start_offset < text_size) {
ret = text_edit_insert_text(mledit->model, offset, text + rows_start_offset);
}
}
}
slist_deinit(&offset_list);
}
if (ret == RET_OK) {
mledit_dispatch_event(widget, EVT_VALUE_CHANGED);
mledit_update_status(widget);
}
return ret;
}
static ret_t mledit_on_add_child(widget_t* widget, widget_t* child) {
mledit_t* mledit = MLEDIT(widget);
const char* type = widget_get_type(child);
return_value_if_fail(mledit != NULL && widget != NULL && child != NULL, RET_BAD_PARAMS);
if (tk_str_eq(type, WIDGET_TYPE_SCROLL_BAR_DESKTOP)) {
widget_on(child, EVT_VALUE_CHANGED, mledit_on_scroll_bar_value_changed, widget);
}
text_edit_set_on_state_changed(mledit->model, mledit_on_text_edit_state_changed, widget);
return RET_CONTINUE;
}
static ret_t mledit_init_idle_func(const idle_info_t* info) {
text_edit_state_t state = {0};
mledit_t* mledit = MLEDIT(info->ctx);
return_value_if_fail(mledit != NULL, RET_BAD_PARAMS);
mledit_set_cursor(WIDGET(mledit), 0);
text_edit_get_state(mledit->model, &state);
mledit_on_text_edit_state_changed(mledit, &state);
return RET_REMOVE;
}
const char* s_mledit_properties[] = {WIDGET_PROP_READONLY,
WIDGET_PROP_CANCELABLE,
WIDGET_PROP_MARGIN,
WIDGET_PROP_LEFT_MARGIN,
WIDGET_PROP_RIGHT_MARGIN,
WIDGET_PROP_TOP_MARGIN,
WIDGET_PROP_BOTTOM_MARGIN,
WIDGET_PROP_TIPS,
WIDGET_PROP_TR_TIPS,
WIDGET_PROP_KEYBOARD,
WIDGET_PROP_OPEN_IM_WHEN_FOCUSED,
WIDGET_PROP_CLOSE_IM_WHEN_BLURED,
MLEDIT_PROP_MAX_LINES,
MLEDIT_PROP_MAX_CHARS,
MLEDIT_PROP_WRAP_WORD,
MLEDIT_PROP_SCROLL_LINE,
MLEDIT_PROP_OVERWRITE,
NULL};
TK_DECL_VTABLE(mledit) = {.size = sizeof(mledit_t),
.type = WIDGET_TYPE_MLEDIT,
.focusable = TRUE,
.inputable = TRUE,
.pointer_cursor = WIDGET_CURSOR_EDIT,
.clone_properties = s_mledit_properties,
.persistent_properties = s_mledit_properties,
.parent = TK_PARENT_VTABLE(widget),
.create = mledit_create,
.on_paint_self = mledit_on_paint_self,
.on_re_translate = mledit_on_re_translate,
.set_prop = mledit_set_prop,
.get_prop = mledit_get_prop,
.on_event = mledit_on_event,
.on_add_child = mledit_on_add_child,
.on_destroy = mledit_on_destroy};
widget_t* mledit_create(widget_t* parent, xy_t x, xy_t y, wh_t w, wh_t h) {
widget_t* widget = widget_create(parent, TK_REF_VTABLE(mledit), x, y, w, h);
mledit_t* mledit = MLEDIT(widget);
return_value_if_fail(mledit != NULL, NULL);
mledit->model = text_edit_create(widget, FALSE);
ENSURE(mledit->model != NULL);
mledit->wrap_word = TRUE;
mledit->margin = 1;
mledit->top_margin = 0;
mledit->left_margin = 0;
mledit->right_margin = 0;
mledit->bottom_margin = 0;
mledit->scroll_line = 1.0f;
wstr_init(&(mledit->temp), 0);
widget_set_text(widget, L"");
mledit->close_im_when_blured = TRUE;
mledit->open_im_when_focused = TRUE;
wstr_init(&(mledit->saved_text), 0);
mledit_update_status(widget);
widget_add_idle(widget, mledit_init_idle_func);
return widget;
}
widget_t* mledit_cast(widget_t* widget) {
return_value_if_fail(WIDGET_IS_INSTANCE_OF(widget, mledit), NULL);
return widget;
}