Can a VID list style contain a field?

Started by Steven White on 7-Nov-2016/12:45:35-8:00
I want to make a form for entering data into a list, and the size of the list is not known until run time. I am wondering if the 'list' style can contain a field for data entry, and how I would go about using it. In the contrived demo below, if I type a couple characters into any field, the program crashes. Even if it did not crash, I don't see how I would get my hands on the field values. The 'count' and 'index' items are available only (it seems) in the 'supply' function which is executed when building the list and not in any post-building processing. Maybe it can't be done; maybe a 'list' is just for 'listing.' But if it can, and anyone knows how, I would be grateful for some direction. Thank you. R E B O L [ Title: 'Test a list with a field' ] ;; This is the data that will go on the list. ;; It is a simple demo, a contrived example to have something ;; to work with. It is a list of names, plus 'nicknames' ;; that we hope to change in a window with a list. NAMES: [ ['Andrew Anderson' 'Andy'] ['Betty Barber' 'Bets'] ['Charles Carlson' 'Chuck'] ['David Davidson' 'Dave'] ['Edward Edwards' 'Ed'] ['Francis Frankenstein' 'Frank'] ] SUPPLY-START: 0 view center-face layout [ across banner 'Test of list with field' return space 0 text snow black 200 'Full name' center bold text snow black 150 'Nickname' center bold return LST: list 350x400 [ origin 0 space 0x0 across text 200 bold field 150 bold ;;; Can I put a field here and what do I do with it? ] supply [ count: count + SUPPLY-START face/color: ivory face/text: none face/font/color: black if even? count [face/color: ivory - 50.50.50] if none? NAME-BLOCK: pick NAMES count [exit] face/text: pick NAME-BLOCK index ] slider 20x400 [ SUPPLY-START: (length? NAMES) * value show LST ] ]
My inclination would be to say this is very, very difficult to pull off. Each list item is actually the SAME block of faces (let's call them 'cast faces') shown at multiple offsets simultaneously (let's call them 'rows' and we'll call the display of the cast faces at each row an 'impression'). The height of each row is fixed (based on the row layout spec) as is the number of rows based on container height / row height. Initially the impression on every row is composed with variations of the cast faces determined by the SUPPLY function. Some events trigger a refresh of a row's impression (usually dependent on offset), other events can trigger the refresh of every row. (you can see this by adding PRINT [AS-PAIR COUNT INDEX FACE/STYLE] to the end of your SUPPLY block). What's happening in your example is that you are able to focus on the field, but then each keypress forces the refresh of all the rows. As the cast faces are used to create each new impression, the SUPPLY function alters their content and the field's edit functions get all confused. A simpler approach would be to have a separate panel to edit each of your fields and just use the list to select which field you are editing. You can do this by attaching actions to each of the cast faces that populate and focus the panel. LST: list 350x400 [ across origin 0 space 0 text 200 bold [probe face/data] text 150 bold [probe face/data] ] supply [ ; ... your supply function, and: ... face/data: name-block/1 ]
Or just simply: face/data: name-block Then replace PROBE with POPULATE EDIT PANEL. FACE/DATA in the functions will be the same block from your data block.
Or better put, replace: probe face/data With: populate-edit-panel face/data Lists contain much sleight of hand so as to be performant. They are that, but it makes them complex. Far better to keep their utility as conservative as possible.
Or, maybe an alternative representation might be to make a subpanel with a column of fields, where the entire face containing the fields can be scrolled with slider. The slider would be in the main face, not the sub panel. It could look like a list, yet every field is unique, so on could set & get the values. I imagine this could be composed on startup, with the number of fields as needed. They might be identified with a variable name, or just by the position in the order of calling all data of the fields of the subpanel. Some other ideas might be gleaned from: http://rebol2.blogspot.com.au/2013/08/the-worlds-smallest-spreadsheet-program.html
Steve, take a look at http://business-programming.com/business_programming.html#section-16.21 Read the section titled '16.21.2 Creating Home Made Multi Column Data Grids'
The section of the tutorial at that link also demonstrates how to accomplish your goal, using the list style. The final example, as well as several simpler examples show how it can be done. I've always just used simpler styles, such as a 'text style, instead of a 'field style, and attached a 'request-text action to each instance of any text I want to edit. Check out the examples and the explanations which lead up to this code, in the section linked above: R E B O L [title: "List Widget Example"] x: copy [] random/seed now/time ; generate 5000 rows of random data: repeat i 5000 [ append/only x reduce [random "asdfqwertyiop" form random 1000 form i] ] y: copy x Alert help-txt: {Be sure to try the following features: 1) Resize the GUI window to see the list automatically adjust to fit 2) Click column headers to sort by field 3) Use the arrow keys and page-up/page-down keys to scroll 4) Use the Insert, Delete and "M" keys to add, remove and move rows (by default, at the currently highlighted row) 5) Click the small "r" header button in the top right corner to reset the list back to its original values 6) Click any individual data cell to edit the selected value.} sort-column: func [field] [ either sort-order: not sort-order [ sort/compare x func [a b] [(at a field) > (at b field)] ] [ sort/compare x func [a b] [(at a field) < (at b field)] ] show li ] key-scroll: func [scroll-amount] [ s-pos: s-pos + scroll-amount if s-pos > (length? x) [s-pos: length? x] if s-pos < 0 [s-pos: 0] sl/data: s-pos / (length? x) show li show sl ] resize-grid: func [percentage] [ gui-size: system/view/screen-face/pane/1/size ; - 10x0 list-size/1: list-size/1 * percentage list-size/2: gui-size/2 - 95 t-size: round (list-size/1 / 3) sl-size: as-pair 16 list-size/2 unview/only gui view/options center-face layout gui-block [resize] ] resize-fit: does [ gui-size: system/view/screen-face/pane/1/size resize-grid (gui-size/1 / list-size/1 - .1) ] insert-event-func [either event/type = 'resize [resize-fit none] [event]] gui-size: system/view/screen-face/size - 0x50 list-size: gui-size - 60x95 sl-size: as-pair 16 list-size/2 t-size: round (list-size/1 / 3) s-pos: 0 sort-order: true ovr-cnt: none svv/vid-face/color: white view/options center-face gui: layout gui-block: [ size gui-size across btn "Smaller" [resize-grid .75] btn "Bigger" [resize-grid 1.3333] btn "Fit" [resize-fit] btn #"^~" "Remove" [attempt [ indx: to-integer request-text/title/default "Row to remove:" form to-integer ovr-cnt if indx = 0 [return] if true <> request rejoin ["Remove: " pick x indx "?"] [return] remove (at x indx) show li ]] insert-btn: btn "Add" [attempt [ indx: to-integer request-text/title/default "Add values at row #:" form to-integer ovr-cnt if indx = 0 [return] new-values: reduce [ request-text request-text (form ((length? x) + 1)) ] insert/only (at x indx) new-values show li ]] btn #"m" "Move" [ old-indx: to-integer request-text/title/default "Move from row #:" form to-integer ovr-cnt new-indx: to-integer request-text/title "Move to row #:" if ((new-indx = 0) or (old-indx = 0)) [return] if true <> request rejoin ["Move: " pick x old-indx "?"] [return] move/to (at x old-indx) new-indx show li ] btn "Save" [save to-file request-file/save x] btn "Load" [y: copy x: copy load request-file/only show li] btn "Read Me" [alert help-txt] btn "View Data" [editor x] return space 0x0 style header button as-pair t-size 20 black white bold header "Random Text" [sort-column 1] header "Random Number" [sort-column 2] header "Unique Key" [sort-column 3] button black "r" 17x20 [if true = request "Reset?"[x: copy y show li]] return li: list list-size [ style cell text t-size feel [ over: func [f o] [ if (o and (ovr-cnt <> f/data)) [ovr-cnt: f/data show li] ] engage: func [f a e] [ if a = 'up [ f/text: request-text/default f/text show li ] ] ] across space 0x0 col1: cell blue col2: cell col3: cell red ] supply [ either even? count [face/color: white] [face/color: 240.240.255] count: count + s-pos if none? q: pick x count [face/text: copy "" exit] if ovr-cnt = count [face/color: 200.200.255] face/data: count face/text: pick q index ] sl: scroller sl-size [s-pos: (length? x) * value show li] key keycode [up] [key-scroll -1] key keycode [down] [key-scroll 1] key keycode [page-up] [key-scroll -20] key keycode [page-down] [key-scroll 20] key keycode [insert] [do-face insert-btn 1] ] [resize]
I made a more easily usable implementation of a version of the code example above: R E B O L [title: "Data Grid Lib Example"] headers: ["Column1" "Column2"] x: data: [ ["Item 01-01" "Item 02-01"] ["Item 01-02" "Item 02-02"] ["Item 01-03" "Item 02-03"] ["Item 01-04" "Item 02-04"] ["Item 01-05" "Item 02-05"] ] do http://re-bol.com/gridlib.r
To answer your question more specifically, replace this line in your example above: field 150 bold ;;; Can I put a field here and what do I do with it? with this: text 150 bold [ face/text: request-text/default face/text show face focus face unfocus face ] There are several examples at the link above which demonstrate that technique. In the big example which I pasted above, it's found in 'feel block of the 'cell style.
There's a lot more in that section of the tutorial which explains how to access the data in an editable grid (search for "editor second second get in (list-widget-label) 'subfunc"), save/load the entire table add/delete rows, etc., which should answer all your questions.

Reply