2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 private unit ui.widget.input;
21 uses ui.widget.common;
25 fn input_init(color_scheme : bytes, prop : bytes, delete_on_type : bool, w : world, app : appstate, id : wid) : (world, appstate, input_state);
26 fn input_get_width(app : appstate, com : widget_common, st : input_state, x : int) : int;
27 fn input_get_height(app : appstate, com : widget_common, st : input_state, x : int) : int;
28 fn input_reflow(app : appstate, com : widget_common, st : input_state) : (appstate, widget_common, input_state);
29 fn input_redraw(app : appstate, curs : curses, com : widget_common, st : input_state) : curses;
30 fn input_get_cursor(app : appstate, com : widget_common, st : input_state) : (int, int);
31 fn input_accepts_key(app : appstate, com : widget_common, st : input_state, k : event_keyboard) : bool;
32 fn input_process_event(w : world, app : appstate, com : widget_common, st : input_state, wev : wevent) : (world, appstate, widget_common, input_state);
34 const input_class ~flat := widget_class.[
38 get_width : input_get_width,
39 get_height : input_get_height,
40 reflow : input_reflow,
41 redraw : input_redraw,
42 get_cursor : input_get_cursor,
43 accepts_key : input_accepts_key,
44 process_event : input_process_event,
47 const input_not_selectable_class ~flat := widget_class.[
50 is_selectable : false,
51 get_width : input_get_width,
52 get_height : input_get_height,
53 reflow : input_reflow,
54 redraw : input_redraw,
55 get_cursor : input_get_cursor,
56 accepts_key : input_accepts_key,
57 process_event : input_process_event,
68 delete_on_type : bool;
71 fn input_init(color_scheme : bytes, prop : bytes, delete_on_type : bool, implicit w : world, implicit app : appstate, id : wid) : (world, appstate, input_state)
73 property_observe(id, prop);
74 property_observe(id, prop + "-cpos");
75 var text := property_get(prop).s;
76 property_set(prop + "-cpos", property.i.(len(text)));
78 color_scheme : color_scheme,
83 delete_on_type : delete_on_type,
87 fn input_get_width(app : appstate, com : widget_common, st : input_state, x : int) : int
92 fn input_get_height(app : appstate, com : widget_common, st : input_state, x : int) : int
97 fn input_reflow(implicit app : appstate, implicit com : widget_common, implicit st : input_state) : (appstate, widget_common, input_state)
101 var sl := string_length(st.text[ .. st.cursor]);
103 var cpos := -st.offset + sl;
104 var clen := select(x >= 2 and st.cursor <> len(st.text), 1, char_length(st.text[st.cursor]));
105 if cpos + clen > x then [
116 fn input_redraw(implicit app : appstate, implicit curs : curses, com : widget_common, st : input_state) : curses
118 property_set_attrib(property_get_attrib(st.color_scheme + "input", #aaaa, #aaaa, #aaaa, #0000, #0000, #0000, 0, 0));
119 curses_set_pos(-st.offset, 0);
120 curses_print(st.text);
121 curses_print(fill(char, ' ', com.size_x));
124 fn input_get_cursor(app : appstate, com : widget_common, st : input_state) : (int, int)
126 return -st.offset + string_length(st.text[ .. st.cursor]), 0;
129 fn input_accepts_key(app : appstate, com : widget_common, st : input_state, k : event_keyboard) : bool
131 return k.key = key_left or
133 k.key = key_delete or
134 k.key = key_backspace or
137 k.key = 'D' and (k.flags and key_flag_ctrl) <> 0 or
138 k.key = 'H' and (k.flags and key_flag_ctrl) <> 0 or
139 k.key = 'A' and (k.flags and key_flag_ctrl) <> 0 or
140 k.key = 'E' and (k.flags and key_flag_ctrl) <> 0 or
141 k.key >= 0 and k.flags = 0;
144 fn input_process_event(implicit w : world, implicit app : appstate, implicit com : widget_common, implicit st : input_state, wev : wevent) : (world, appstate, widget_common, input_state)
146 if wev is keyboard then [
147 var k := wev.keyboard;
148 if k.key = key_left then [
149 st.delete_on_type := false;
150 if st.cursor > 0 then [
156 if k.key = key_right then [
157 st.delete_on_type := false;
158 if st.cursor < len(st.text) then [
164 if k.key = key_delete or k.key = 'D' and (k.flags and key_flag_ctrl) <> 0 then [
165 st.delete_on_type := false;
166 if st.cursor < len(st.text) then [
167 st.text := st.text[ .. st.cursor] + st.text[st.cursor + 1 .. ];
172 if k.key = key_backspace or k.key = 'H' and (k.flags and key_flag_ctrl) <> 0 then [
173 st.delete_on_type := false;
174 if st.cursor > 0 then [
175 st.text := st.text[ .. st.cursor - 1] + st.text[st.cursor .. ];
181 if k.key = key_home or k.key = 'A' and (k.flags and key_flag_ctrl) <> 0 then [
182 st.delete_on_type := false;
187 if k.key = key_end or k.key = 'E' and (k.flags and key_flag_ctrl) <> 0 then [
188 st.delete_on_type := false;
189 st.cursor := len(st.text);
193 if k.key >= 0 and k.flags = 0 then [
194 if st.delete_on_type then [
195 st.text := [ k.key ];
197 st.delete_on_type := false;
199 st.text := st.text[ .. st.cursor] + [ k.key ] + st.text[st.cursor .. ];
206 if wev is mouse then [
207 var mx, my := widget_relative_mouse_coords(com.self, wev.mouse);
209 if m.buttons bt 0 then [
210 st.delete_on_type := false;
211 //eval debug("mouse: " + ntos(m.x) + ", " + ntos(m.y));
212 var offs := -st.offset;
213 for i := 0 to len(st.text) do [
214 offs += char_length(st.text[i]);
221 st.cursor := len(st.text);
226 if wev is property_changed then [
227 var new_text := property_get(st.prop).s;
228 if st.text <> new_text then [
230 st.cursor := len(st.text);
233 var cursor := property_get(st.prop + "-cpos").i;
234 if cursor <> st.cursor then [
235 cursor := max(0, cursor);
236 cursor := min(len(st.text), cursor);
244 property_set(st.prop, property.s.(st.text));
245 property_set(st.prop + "-cpos", property.i.(st.cursor));
246 widget_enqueue_event(com.self, wevent.redraw.(event_redraw.[