initial checkin
[org.ibex.widgets.git] / src / ibex / lib / text / flow.t
1 <ibex>
2     <org.ibex.theme.win2k.focusable /> // HACK: should be in a better place
3     <ui:box align="topleft" fill="green" packed="false">
4         <ui:box id="content" rows="0" cols="1" shrink="true" align="topleft" fill="yellow" />
5
6         var packing = -1;
7
8         SizeChange ++= function(v) {
9             ibex.thread = function() { $content.maxwidth = thisbox.width; }
10             pack(0);
11         }
12         // TODO: align ++= function(a) { for (var i=0; $content.numchildren > i; i++) $content[i].align = a; }
13
14
15         var currentfocus = null; // HACK: for subfocus model
16         thisbox._KeyPressed ++= function(v) { 
17             if (currentfocus) currentfocus.KeyPressed = v;
18             return true;
19         }
20
21         // creates an empty row, ready for insertion into $content
22         thisbox.row = function() {
23             var b = ibex.box;
24             b.shrink = true;
25             //b.align = thisbox.align;
26             b.align = "topleft";
27
28             var packtrap = function(v) { pack($content.indexof(b)); }
29             b.SizeChange ++= packtrap;
30
31             // ------------------------------------------------------
32             // HACK: quick and dirty subfocusing for text
33
34             var block_prevfocus = function() {
35                 // find prev focusable in current row
36                 var index = b.indexof(trapee);
37                 if (index > 0) return b[index - 1];
38
39                 // look in prev row
40                 var r = $content.indexof(b);
41                 if (r > 0 and $content[r - 1].numchildren > 0) return $content[r - 1][($content[r - 1].numchildren)];
42             }
43             var block_nextfocus = function() {
44                 ibex.log.info("block_nextfocus: finding next focus");
45                 // find next focusable in current row
46                 var index = b.indexof(trapee);
47                 if (b.numchildren - 1 > index) return b[index + 1];
48
49                 // look in next row
50                 var r = $content.indexof(b);
51                 if ($content.numchildren - 1 > r and $content[r + 1].numchildren > 0) return $content[r + 1][0];
52             }
53             var block_curpos = function(v) {
54                 if (0 > v) {
55                     var movefocus = trapee.parent.prevfocus; // usage of HACK mentioned below
56                     if (movefocus) {
57                         trapee.parent.focused = false; movefocus.cur.pos = movefocus.text.length;
58                         movefocus.focused = true; currentfocus = movefocus;
59                     }
60                 } else if (v > trapee.parent.text.length) {
61                     ibex.log.info("stepped over right-hand block boundry");
62                     var movefocus = trapee.parent.nextfocus;
63                     if (movefocus) {
64                         ibex.log.info("changing focus");
65                         trapee.parent.focused = false; movefocus.cur.pos = 0;
66                         movefocus.focused = true; currentfocus = movefocus;
67                     }
68                 }
69             }
70             var block_press1 = function(c) {
71                 if (currentfocus != trapee) {
72                     if (currentfocus) currentfocus.focused = false;
73                     trapee.focused = true;
74                     currentfocus = trapee;
75                 }
76             }
77             b.childadded ++= function(c) {
78                 c.prevfocus ++= block_prevfocus;
79                 c.nextfocus ++= block_nextfocus;
80                 c.cur.pos ++= block_curpos;
81                 c.Press1 ++= block_press1;
82                 c.cur.parent = c; // HACK: need for block_curpos above
83             }
84             b.childremoved ++= function(c) {
85                 c.prevfocus --= block_prevfocus;
86                 c.nextfocus --= block_nextfocus;
87                 c.cur.pos --= block_curpos;
88                 c.Press1 --= block_press1;
89             }
90             // ------------------------------------------------------
91
92             return b;
93         }
94
95         // HACK: clean this up conceptually
96         childadded ++= function(newc) { if (thisbox.indexof(newc) >= 0) add(newc); }
97
98         // add a block of text to the flow element
99         thisbox.add = function(b) {
100             if (1 > $content.numchildren) $content[0] = row();
101             var r = $content[$content.numchildren -1];
102
103             // HACK: for subfocus model
104             if (currentfocus) b.focused = false;
105             else { b.focused = true; currentfocus = b; }
106
107             r[r.numchildren] = b;
108         }
109
110         // launch the packNow function in a background thread
111         var pack = function(ind) {
112             if (packing == -1) {
113                 packing = ind;
114                 ibex.thread = packNow;
115             } else if (packing > ind) {
116                 // background thread already queued, so just
117                 // update location from which to start
118                 packing = ind;
119             }
120         }
121
122         // pack the contents of this flow starting at row matching value of packing
123         var packNow = function() {
124             var ind = packing;
125             if (0 > ind) return;
126
127             // work through each row, packing them
128             ROW: for (; $content.numchildren > ind; ind++) {
129                 ibex.log.debug("tag 2, ind="+ind);
130                 var r = $content[ind];
131
132                 // calculate excess width of row contents (negative to represent free space)
133                 var w = 0;
134                 for (var i=0; r.numchildren > i; i++) w += r[i].width;
135                 w -= $content.maxwidth;
136
137                 var nextr = ind+1;
138                 ibex.log.debug("tag 3, w="+w+", nextr="+nextr+", $content.numchildren="+$content.numchildren);
139
140                 if (0 > w and $content.numchildren > nextr) {
141                     // attempt to pack contents from next row into current row
142                     w  = -1 * w;
143
144                     // find first non-empty following row
145                     var n = $content[nextr];
146                     ibex.log.debug("tag 4, n==null?"+(n==null));
147                     while (n != null and 1 > n.numchildren and $content.numchildren > nextr) {
148                         n.thisbox = null; n = $content[nextr];
149                     }
150                     ibex.log.debug("tag 5 n.numchildren="+(n==null?-1:n.numchildren));
151                     if (n == null || 1 > n.numchildren) break ROW;
152
153                     // move up excess blocks
154                     while (w > n[0].width) {
155                         ibex.log.debug("r["+r.numchildren+"] = n[0] (n.numchildren="+n.numchildren+")");
156                         w -= n[0].width; r[r.numchildren] = n[0];
157                         debugState();
158                         while (n.numchildren == 0) {
159                             $content[nextr] = null;
160                             debugState();
161                             if (nextr >= $content.numchildren) break ROW;
162                             n = $content[nextr];
163                         }
164                     }
165
166                     ibex.log.debug("tag 6");
167
168                     // attempt to split up final block
169                     if (n.numchildren > 0 and n[0].splitable) {
170                         var splitb = n[0].split(w);
171                         if (splitb) r[r.numchildren] = splitb;
172                     }
173
174                 } else if (w > 0) {
175                     // push row contents to next row
176                     if (nextr == $content.numchildren) $content[$content.numchildren] = row();
177
178                     // move off excess blocks
179                     var b;
180                     for (b = r[r.numchildren -1]; b != null and w > b.width; b = r[r.numchildren -1]) {
181                         $content[nextr][0] = b;
182                         w -= b.width;
183                     }
184
185                     if (b == null) continue ROW;
186
187                     if (b.splitable) {
188                         // attempt to split up final block
189                         var newb = b.split(b.width - w);
190                         if (newb != null) {
191                             b.thisbox = null;
192                             r[r.numchildren] = newb;
193                             $content[nextr][0] = b;
194                             continue;
195                         }
196                     }
197
198                     // split failed
199                     if (r.numchildren > 1) $content[nextr][0] = b;
200                     else ibex.log.warn("box exceeds window limit"); // FIXME: how do we handle this?
201
202                     ibex.log.debug("tag 10");
203                 }
204
205             }
206
207             //ibex.log.debug(debugState());
208             packing = -1;
209         }
210
211         var debugState = function() {
212             var str = "---- DEBUG $content child="+$content.numchildren
213                 + "maxwidth="+$content.width+", width="+$content.width+", height="+$content.height+"\n";
214             for (var i=0; $content.numchildren > i; i++) {
215                 str += "$content["+i+"].numchildren="+$content[i].numchildren + "\n";
216                 for (var j=0; $content[i].numchildren > j; j++) {
217                     str += "$content["+i+"]["+j+"] text="+$content[i][j].text
218                         + " width="+$content[i][j].width+", height="+$content[i][j].height+"\n";
219                 }
220             }
221             str += "----";
222             return str;
223         }
224
225     </ui:box>
226 </ibex>