1 <!-- Copyright 2002 NeuronForge Pty Ltd, see COPYING file for licensing [LGPL] -->
3 A single-line or multi-line edit widget. Only handles text with one font and one color, and is capable of line or word wrapping.
7 - multiline : boolean -- If true, edit widget will expand vertically to handle newlines.
8 - editable : boolean -- If true, the user can insert, paste and delete text.
9 - disabled : boolean -- If false, user can select text and copy to clipboard.
10 - wrap : string -- Either "none", "line" or "word", specifing the form of text wrap to use.
11 - selection : string -- Returns the currently selected text. Putting values does nothing, see FUNCTIONS, selectText().
12 - text : string -- Represents the complete text of this edit widget. Can be read and written to.
13 - limit : int -- A limit imposed on the total number of characters in the edit widget. 0 > limit means no limit.
14 - textcolor : color -- Color of the text.
15 - selectcolor : color -- Background Color of the currently selected text.
16 - selecttextcolor : color -- Color of the currently selected text.
17 - textChanged : boolean -- Set to true when the contents of the edit widget has changed.
22 If you wish to directly manipulate the contents of the edit widget consider using these functions to speed up manipulation.
23 All line references are based on hard carrige returns. You do not have to consider soft line wraps when making calculations.
25 - insertText(line, index, text) - Insert text at given char index on given line.
26 - deleteText(startline, startindex, endline, endindex) - Delete text in given range.
27 - selectText(startline, startindex, endline, endindex) - Select text in given range.
28 - clearSelection() - Deselect any current selection.
29 - deleteSelection() - Delete the text within the current selection range.
30 - moveCursor(line, index) - Move the cursor to the given position.
35 - Most of the implementation of this widget does not need to be considered in a theme, however a particular theme may wish
36 to override _KeyPressed to add extra theme features (eg. Monopoly word skip on Ctrl+Left Arrow).
41 - In the implementation of the edit widget, there are two systems (loosely similar to model-view) used to reference text.
43 The first, referencing each 'hard' line (from one carrige return to the next) as a box inside $content
44 with the string property fulltext representing the contents of the row is used as the text model.
45 Text is inserted, removed and read using the $content[cl].fulltext properties.
47 The second is the on screen reprsentation of the text. Each $content[cl] box can have any number of children ( must be > 0)
48 called 'soft' lines. These softlines are created/managed at the discretion of the $content[cl]._fulltext trap. This means
49 different wrapping systems can be completely isolated inside the _fulltext trap. The only components of the system that
50 work outside of the model are cursor and selection positioning. The functions that manipulate these features have public
51 functions that mimic the 'model' style functions, but also have private internal functions that use cy and px variables
52 to reference location.
54 - A reference to each cursor is stored in the static area, and a global thread is used to call the _blink trap on cursors.
56 - Boxes are also stored in a static array when unused, however the effective value of this is questionable, and should be
59 - If moving the cursor to a different cl line after changing the structure of the edit box, be sure to call the funciton from
60 a background thread. Otherwise, the position x,y values will be wrong.
64 - keep length value up to date so limit checking works.
67 <preapply name="org.xwt.builtin.edit_lib"/>
69 <template multiline="false" disabled="false" editable="true" wrap="none" font="sansserif"
70 selectcolor="blue" selecttextcolor="#FFFFFF" color="#FFFFFF" orient="vertical">
77 // Handles key events that occur in the surrounding whitespace.
78 $vspace._Press1 = function(v) { ref_press(content[content.numchildren -1]); }
79 $hspace._Press1 = function(v) {
80 for (var i=0; content.numchildren > i; i++) {
81 if (content[i].y + content[i].height >= content.mousey) { ref_press(content[i]); return; }
83 ref_press(content[content.numchildren -1]);
86 _focused = function(f) {
87 curs.focused = f; arguments.cascade(f); curs.blink = false;
88 if (!f) { clearSelection(); }
91 _disabled = function(d) { if (d) { cursor = "default"; } else { cursor = "text"; } }
93 _keypress = function(k) {
94 if (k == null || disabled) return;
96 var key = k.substring(k.lastIndexOf('-')+1).toLowerCase();
98 if (key.length == 1) {
101 selectText(0, 0, content.numchildren -1, content[content.numchildren -1].fulltext.length);
102 arguments.cascade(null);
103 } else if (key == 'x') {
104 xwt.clipboard = selection; deleteSelection();
105 arguments.cascade(null);
106 } else if (key == 'c') {
107 xwt.clipboard = selection;
108 arguments.cascade(null);
109 } else if (key == 'v') {
110 deleteSelection(); insertText(curs.cl, curs.cx, xwt.clipboard);
112 arguments.cascade(null);
114 arguments.cascade(k);
117 arguments.cascade(k);
119 } else if (key == "left") {
123 // Skip word algorithm. Ugly to look at.
124 cl = curs.cl; cx = curs.cx;
127 for (cx--; cx >= 0; cx--) { if (content[cl].fulltext.charAt(cx) != ' ') break; }
128 if (0 > cx) { if (cl > 0) { cl--; cx = content[cl].fulltext.length; } else { break; } }
130 findendchar: for (cx--; cx >= 0; cx--) {
131 switch (content[cl].fulltext.charAt(cx)) {
132 case ' ': break findendchar;
133 case '-': break findendchar;
139 // Use right boundry of selection, otherwise use the cursor.
140 if (sel1.cl != -1) { cl = sel1.cl; cx = calcCx(cl, sel1.cy, sel1.px) -1; }
141 else { cl = curs.cl; cx = curs.cx -1; }
144 if (xwt.shift) { updateSelectionCx(cl, cx); }
145 else { clearSelection(); moveCursor(cl, cx); }
146 arguments.cascade(null);
148 } else if (key == "right") {
152 // Skip word algorithm. Ugly to look at.
153 cl = curs.cl; cx = curs.cx;
156 for (cx++; content[cl].fulltext.length > cx; cx++) { if (content[cl].fulltext.charAt(cx) != ' ') break; }
157 if (cx > content[cl].fulltext.length) { if (content.numchildren > cl) { cl++; cx = 0; } else { break; } }
159 findendchar: for (cx++; content[cl].fulltext.length > cx; cx++) {
160 switch (content[cl].fulltext.charAt(cx)) {
161 case ' ': break findendchar;
162 case '-': break findendchar;
168 // Use right boundry of selection, otherwise use the cursor.
169 if (sel2.cl != -1) { cl = sel2.cl; cx = calcCx(cl, sel2.cy, sel2.px) +1; }
170 else { cl = curs.cl; cx = curs.cx +1; }
173 if (xwt.shift) { updateSelectionCx(cl, cx); }
174 else { clearSelection(); moveCursor(cl, cx); }
175 arguments.cascade(null);
177 } else if (key == "down") {
179 if (xwt.control) { cl = content.numchildren -1; cy = content[cl].numchildren -1; }
180 else { if (curs.cy == content[curs.cl].numchildren -1) { cl = curs.cl +1; cy = 0; } else { cl = curs.cl; cy = curs.cy + 1; } }
182 if (xwt.shift) { updateSelection(cl, cy, curs.px); }
183 else { var px = curs.px; clearSelection(); moveCursorToCy(cl, cy, px); }
184 arguments.cascade(null);
186 } else if (key == "up") {
188 if (xwt.control) { cl = 0; cy = content[0].numchildren -1; }
190 if (curs.cy == 0) { if (curs.cl == 0) { cl=0; cy=0; } else { cl = curs.cl -1; cy = content[cl].numchildren -1; } }
191 else { cl = curs.cl; cy = curs.cy -1; }
194 if (xwt.shift) { updateSelection(cl, cy, curs.px); }
195 else { var px = curs.px; clearSelection(); moveCursorToCy(cl, cy, px); }
196 arguments.cascade(null);
198 } else if (key == "home") {
200 if (xwt.control) { cy = 0; }
201 else { cy = curs.cy; }
203 if (xwt.shift) { updateSelection(curs.cl, cy, 0); }
204 else { var cl = curs.cl; clearSelection(); moveCursorToCy(cl, cy, 0); }
205 arguments.cascade(null);
207 } else if (key == "end") {
209 if (xwt.control) { cy = content[curs.cl].numchildren -1; }
210 else { cy = curs.cy; }
212 if (xwt.shift) { updateSelection(curs.cl, cy, content[curs.cl][cy].text.length); }
213 else { var cl = curs.cl; clearSelection(); moveCursorToCy(cl, cy, content[cl][cy].text.length); }
214 arguments.cascade(null);
216 } else if (key == "insert") {
217 xwt.println("NOT YET IMPLEMENTED: insert key"); // TODO
218 arguments.cascade(null);
222 <box orient="horizontal">
223 <box id="content" hpad="0" vpad="0" align="topleft" orient="vertical" shrink="true"/>
228 <box absolute="true" id="sel1" cl="0" cy="0" px="0" shrink="true"/>
229 <box absolute="true" id="sel2" cl="0" cy="0" px="0" shrink="true"/>
230 <box absolute="true" id="curs" cl="0" cx="0" cy="0" px="0" width="1" blink="false" color="black">
231 _blink = function(v) { invisible = (focused and !disabled) ? v : true; }