https://github.com/akkartik/mu1/blob/master/edit/006-sandbox-copy.mu
  1 ## the 'copy' button makes it easy to duplicate a sandbox, and thence to
  2 ## see code operate in multiple situations
  3 
  4 scenario copy-a-sandbox-to-editor [
  5   local-scope
  6   trace-until 100/app  # trace too long
  7   assume-screen 100/width, 10/height
  8   # empty recipes
  9   assume-resources [
 10   ]
 11   env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
 12   render-all screen, env, render
 13   # run it
 14   assume-console [
 15     press F4
 16   ]
 17   event-loop screen, console, env, resources
 18   screen-should-contain [
 19     .                                                                                 run (F4)           .
 20     .                                                  ╎                                                 .
 21     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 22     .                                                  ╎0   edit       copy       to recipe    delete    .
 23     .                                                  ╎add 1, 1                                         .
 24     .                                                  ╎2                                                .
 25     .                                                  ╎─────────────────────────────────────────────────.
 26     .                                                  ╎                                                 .
 27   ]
 28   # click at left edge of 'copy' button
 29   assume-console [
 30     left-click 3, 69
 31   ]
 32   run [
 33     event-loop screen, console, env, resources
 34   ]
 35   # it copies into editor
 36   screen-should-contain [
 37     .                                                                                 run (F4)           .
 38     .                                                  ╎add 1, 1                                         .
 39     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 40     .                                                  ╎0   edit       copy       to recipe    delete    .
 41     .                                                  ╎add 1, 1                                         .
 42     .                                                  ╎2                                                .
 43     .                                                  ╎─────────────────────────────────────────────────.
 44     .                                                  ╎                                                 .
 45   ]
 46   # cursor should be in the right place
 47   assume-console [
 48     type [0]
 49   ]
 50   run [
 51     event-loop screen, console, env, resources
 52   ]
 53   screen-should-contain [
 54     .                                                                                 run (F4)           .
 55     .                                                  ╎0add 1, 1                                        .
 56     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 57     .                                                  ╎0   edit       copy       to recipe    delete    .
 58     .                                                  ╎add 1, 1                                         .
 59     .                                                  ╎2                                                .
 60     .                                                  ╎─────────────────────────────────────────────────.
 61     .                                                  ╎                                                 .
 62   ]
 63 ]
 64 
 65 scenario copy-a-sandbox-to-editor-2 [
 66   local-scope
 67   trace-until 100/app  # trace too long
 68   assume-screen 100/width, 10/height
 69   # empty recipes
 70   assume-resources [
 71   ]
 72   env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
 73   render-all screen, env, render
 74   # run it
 75   assume-console [
 76     press F4
 77   ]
 78   event-loop screen, console, env, resources
 79   screen-should-contain [
 80     .                                                                                 run (F4)           .
 81     .                                                  ╎                                                 .
 82     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
 83     .                                                  ╎0   edit       copy       to recipe    delete    .
 84     .                                                  ╎add 1, 1                                         .
 85     .                                                  ╎2                                                .
 86     .                                                  ╎─────────────────────────────────────────────────.
 87     .                                                  ╎                                                 .
 88   ]
 89   # click at right edge of 'copy' button (just before 'delete')
 90   assume-console [
 91     left-click 3, 76
 92   ]
 93   run [
 94     event-loop screen, console, env, resources
 95   ]
 96   # it copies into editor
 97   screen-should-contain [
 98     .                                                                                 run (F4)           .
 99     .                                                  ╎add 1, 1                                         .
100     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
101     .                                                  ╎0   edit       copy       to recipe    delete    .
102     .                                                  ╎add 1, 1                                         .
103     .                                                  ╎2                                                .
104     .                                                  ╎─────────────────────────────────────────────────.
105     .                                                  ╎                                                 .
106   ]
107   # cursor should be in the right place
108   assume-console [
109     type [0]
110   ]
111   run [
112     event-loop screen, console, env, resources
113   ]
114   screen-should-contain [
115     .                                                                                 run (F4)           .
116     .                                                  ╎0add 1, 1                                        .
117     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
118     .                                                  ╎0   edit       copy       to recipe    delete    .
119     .                                                  ╎add 1, 1                                         .
120     .                                                  ╎2                                                .
121     .                                                  ╎─────────────────────────────────────────────────.
122     .                                                  ╎                                                 .
123   ]
124 ]
125 
126 after <global-touch> [
127   # support 'copy' button
128   {
129     copy?:bool <- should-attempt-copy? click-row, click-column, env
130     break-unless copy?
131     copy?, env <- try-copy-sandbox click-row, env
132     break-unless copy?
133     screen <- render-sandbox-side screen, env, render
134     screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
135     loop +next-event
136   }
137 ]
138 
139 # some preconditions for attempting to copy a sandbox
140 def should-attempt-copy? click-row:num, click-column:num, env:&:environment -> result:bool [
141   local-scope
142   load-inputs
143   # are we below the sandbox editor?
144   click-sandbox-area?:bool <- click-on-sandbox-area? click-row, click-column, env
145   return-unless click-sandbox-area?, false
146   # narrower, is the click in the columns spanning the 'copy' button?
147   first-sandbox:&:editor <- get *env, current-sandbox:offset
148   assert first-sandbox, [!!]
149   sandbox-left-margin:num <- get *first-sandbox, left:offset
150   sandbox-right-margin:num <- get *first-sandbox, right:offset
151   _, _, copy-button-left:num, copy-button-right:num <- sandbox-menu-columns sandbox-left-margin, sandbox-right-margin
152   copy-button-vertical-area?:bool <- within-range? click-column, copy-button-left, copy-button-right
153   return-unless copy-button-vertical-area?, false
154   # finally, is sandbox editor empty?
155   current-sandbox:&:editor <- get *env, current-sandbox:offset
156   result <- empty-editor? current-sandbox
157 ]
158 
159 def try-copy-sandbox click-row:num, env:&:environment -> clicked-on-copy-button?:bool, env:&:environment [
160   local-scope
161   load-inputs
162   # identify the sandbox to copy, if the click was actually on the 'copy' button
163   sandbox:&:sandbox <- find-sandbox env, click-row
164   return-unless sandbox, false
165   clicked-on-copy-button? <- copy true
166   text:text <- get *sandbox, data:offset
167   current-sandbox:&:editor <- get *env, current-sandbox:offset
168   current-sandbox <- insert-text current-sandbox, text
169   # reset scroll
170   *env <- put *env, render-from:offset, -1
171   # position cursor in sandbox editor
172   *env <- put *env, sandbox-in-focus?:offset, true
173 ]
174 
175 def find-sandbox env:&:environment, click-row:num -> result:&:sandbox [
176   local-scope
177   load-inputs
178   curr-sandbox:&:sandbox <- get *env, sandbox:offset
179   {
180     break-unless curr-sandbox
181     start:num <- get *curr-sandbox, starting-row-on-screen:offset
182     found?:bool <- equal click-row, start
183     return-if found?, curr-sandbox
184     curr-sandbox <- get *curr-sandbox, next-sandbox:offset
185     loop
186   }
187   return null/not-found
188 ]
189 
190 def click-on-sandbox-area? click-row:num, click-column:num, env:&:environment -> result:bool [
191   local-scope
192   load-inputs
193   current-sandbox:&:editor <- get *env, current-sandbox:offset
194   sandbox-left-margin:num <- get *current-sandbox, left:offset
195   on-sandbox-side?:bool <- greater-or-equal click-column, sandbox-left-margin
196   return-unless on-sandbox-side?, false
197   first-sandbox:&:sandbox <- get *env, sandbox:offset
198   return-unless first-sandbox, false
199   first-sandbox-begins:num <- get *first-sandbox, starting-row-on-screen:offset
200   result <- greater-or-equal click-row, first-sandbox-begins
201 ]
202 
203 def empty-editor? editor:&:editor -> result:bool [
204   local-scope
205   load-inputs
206   head:&:duplex-list:char <- get *editor, data:offset
207   first:&:duplex-list:char <- next head
208   result <- not first
209 ]
210 
211 def within-range? x:num, low:num, high:num -> result:bool [
212   local-scope
213   load-inputs
214   not-too-far-left?:bool <- greater-or-equal x, low
215   not-too-far-right?:bool <- lesser-or-equal x, high
216   result <- and not-too-far-left? not-too-far-right?
217 ]
218 
219 scenario copy-fails-if-sandbox-editor-not-empty [
220   local-scope
221   trace-until 100/app  # trace too long
222   assume-screen 100/width, 10/height
223   # empty recipes
224   assume-resources [
225   ]
226   env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
227   render-all screen, env, render
228   # run it
229   assume-console [
230     press F4
231   ]
232   event-loop screen, console, env, resources
233   screen-should-contain [
234     .                                                                                 run (F4)           .
235     .                                                  ╎                                                 .
236     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
237     .                                                  ╎0   edit       copy       to recipe    delete    .
238     .                                                  ╎add 1, 1                                         .
239     .                                                  ╎2                                                .
240     .                                                  ╎─────────────────────────────────────────────────.
241     .                                                  ╎                                                 .
242   ]
243   # type something into the sandbox editor, then click on the 'copy' button
244   assume-console [
245     left-click 2, 70  # put cursor in sandbox editor
246     type [0]  # type something
247     left-click 3, 70  # click 'copy' button
248   ]
249   run [
250     event-loop screen, console, env, resources
251   ]
252   # copy doesn't happen
253   screen-should-contain [
254     .                                                                                 run (F4)           .
255     .                                                  ╎0                                                .
256     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
257     .                                                  ╎0   edit       copy       to recipe    delete    .
258     .                                                  ╎add 1, 1                                         .
259     .                                                  ╎2                                                .
260     .                                                  ╎─────────────────────────────────────────────────.
261     .                                                  ╎                                                 .
262   ]
263   # cursor should be in the right place
264   assume-console [
265     type [1]
266   ]
267   run [
268     event-loop screen, console, env, resources
269   ]
270   screen-should-contain [
271     .                                                                                 run (F4)           .
272     .                                                  ╎01                                               .
273     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
274     .                                                  ╎0   edit       copy       to recipe    delete    .
275     .                                                  ╎add 1, 1                                         .
276     .                                                  ╎2                                                .
277     .                                                  ╎─────────────────────────────────────────────────.
278     .                                                  ╎                                                 .
279   ]
280 ]
281 
282 ## the 'to recipe' button makes it easy to create a function out of a sandbox
283 
284 scenario copy-a-sandbox-to-recipe-side [
285   local-scope
286   trace-until 100/app  # trace too long
287   assume-screen 100/width, 10/height
288   # empty recipes
289   assume-resources [
290   ]
291   env:&:environment <- new-programming-environment resources, screen, [add 1, 1]  # contents of sandbox editor
292   render-all screen, env, render
293   # run it
294   assume-console [
295     press F4
296   ]
297   event-loop screen, console, env, resources
298   screen-should-contain [
299     .                                                                                 run (F4)           .
300     .                                                  ╎                                                 .
301     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎─────────────────────────────────────────────────.
302     .                                                  ╎0   edit       copy       to recipe    delete    .
303     .                                                  ╎add 1, 1                                         .
304     .                                                  ╎2                                                .
305     .                                                  ╎─────────────────────────────────────────────────.
306     .                                                  ╎                                                 .
307   ]
308   # click at left edge of 'copy' button
309   assume-console [
310     left-click 3, 78
311   ]
312   run [
313     event-loop screen, console, env, resources
314   ]
315   # it copies into recipe side
316   screen-should-contain [
317     .                                                                                 run (F4)           .
318     .add 1, 1                                          ╎                                                 .
319     .                                                  ╎─────────────────────────────────────────────────.
320     .                                                  ╎0   edit       copy       to recipe    delete    .
321     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎add 1, 1                                         .
322     .                                                  ╎2                                                .
323     .                                                  ╎─────────────────────────────────────────────────.
324     .                                                  ╎                                                 .
325   ]
326   # cursor should be at the top left of the recipe side
327   assume-console [
328     type [0]
329   ]
330   run [
331     event-loop screen, console, env, resources
332   ]
333   screen-should-contain [
334     .                                                                                 run (F4)           .
335     .0add 1, 1                                         ╎                                                 .
336     .                                                  ╎─────────────────────────────────────────────────.
337     .                                                  ╎0   edit       copy       to recipe    delete    .
338     .╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╎add 1, 1                                         .
339     .                                                  ╎2                                                .
340     .                                                  ╎─────────────────────────────────────────────────.
341     .                                                  ╎                                                 .
342   ]
343 ]
344 
345 after <global-touch> [
346   # support 'copy to recipe' button
347   {
348     copy?:bool <- should-copy-to-recipe? click-row, click-column, env
349     break-unless copy?
350     modified?:bool <- prepend-sandbox-into-recipe-side click-row, env
351     break-unless modified?
352     *env <- put *env, sandbox-in-focus?:offset, false
353     screen <- render-recipes screen, env, render
354     screen <- update-cursor screen, recipes, current-sandbox, sandbox-in-focus?, env
355     loop +next-event
356   }
357 ]
358 
359 # some preconditions for attempting to copy a sandbox into the recipe side
360 def should-copy-to-recipe? click-row:num, click-column:num, env:&:environment -> result:bool [
361   local-scope
362   load-inputs
363   # are we below the sandbox editor?
364   click-sandbox-area?:bool <- click-on-sandbox-area? click-row, click-column, env
365   return-unless click-sandbox-area?, false
366   # narrower, is the click in the columns spanning the 'copy' button?
367   first-sandbox:&:editor <- get *env, current-sandbox:offset
368   assert first-sandbox, [!!]
369   sandbox-left-margin:num <- get *first-sandbox, left:offset
370   sandbox-right-margin:num <- get *first-sandbox, right:offset
371   _, _, _, _, recipe-button-left:num, recipe-button-right:num <- sandbox-menu-columns sandbox-left-margin, sandbox-right-margin
372   result <- within-range? click-column, recipe-button-left, recipe-button-right
373 ]
374 
375 def prepend-sandbox-into-recipe-side click-row:num, env:&:environment -> clicked-on-copy-to-recipe-button?:bool, env:&:environment [
376   local-scope
377   load-inputs
378   sandbox:&:sandbox <- find-sandbox env, click-row
379   return-unless sandbox, false
380   recipe-editor:&:editor <- get *env, recipes:offset
381   recipe-data:&:duplex-list:char <- get *recipe-editor, data:offset
382   # make the newly inserted code easy to delineate
383   newline:char <- copy 10
384   insert newline, recipe-data
385   insert newline, recipe-data
386   # insert code from the selected sandbox
387   sandbox-data:text <- get *sandbox, data:offset
388   insert recipe-data, sandbox-data
389   # reset cursor
390   *recipe-editor <- put *recipe-editor, top-of-screen:offset, recipe-data
391   *recipe-editor <- put *recipe-editor, before-cursor:offset, recipe-data
392   *recipe-editor <- put *recipe-editor, cursor-row:offset, 1
393   *recipe-editor <- put *recipe-editor, cursor-column:offset, 0
394   return true
395 ]