table: make rebface [ tip: { USAGE: table options ["Name" left .6 "Age" right .4] data ["Bob" 32 "Pete" 45 "Jack" 29] DESCRIPTION: Columns and rows of values formatted according to a header definition block. OPTIONS: 'multi allows multiple rows to be selected at once 'no-dividers hides column dividers ["Title" align width] triplets for each column } size: 50x25 pane: [] data: [] edge: default-edge ; widget facets redraw: make function! [] [] ; place-holder for examine selected: make function! [] [] ; place-holder for examine picked: [] widths: [] aligns: [] cols: none rows: make function! [] [pane/1/rows] ;BEG fixed by Cyphre, sponsored by Robert total-width: none add-row: func [ row [block!] /position pos [integer!] ][ either pos [ pos: (pos - 1) * cols ][ pos: 1 + length? data ] insert at data pos row redraw ] remove-row: func [ row [integer! block!] /local rows removed ][ if integer? row [row: to-block row] rows: sort/reverse copy row repeat n length? rows [ row: max 1 min rows/:n (length? data) / cols remove/part skip data (row - 1) * cols cols ] redraw ] alter-row: func [ row [integer! block!] values [block!] /local rows last-picked ][ last-picked: copy picked if integer? row [row: to-block row] rows: row if (length? rows) <> (length? values) [ values: reduce [values] ] if (length? rows) = (length? values) [ repeat n length? rows [ row: max 1 min rows/:n (length? data) / cols change skip data (row - 1) * cols copy/part values/:n cols ] ] redraw unless empty? last-picked [select-row/no-action last-picked] ] select-row: func [ row [integer! none! block!] /no-action /local rows lines ][ clear picked ; #45 AGT handle none! case if row [ row: either integer? row [to block! row] [sort copy row] rows: pane/1/rows lines: pane/1/lines foreach r row [ r: max 1 min rows r insert picked r ] if any [ row/1 < (pane/1/scroll + 1) row/1 > (pane/1/scroll + pane/1/lines) ][ pane/1/pane/2/data: 1 / (rows - lines) * ((min (rows - lines + 1) row/1) - 1) ] unless no-action [action/on-click self] ] ;activate the table keys control - Cyphre system/view/caret: pane/1/pane/1/text system/view/focal-face: pane/1/pane/1 show self ] set-columns: func [ options [block!] /no-show /no-dividers /local col-offset p last-col dividers? ] [ p: self if (length? pane) > 2 [ remove/part next pane 2 * cols - 1 ] clear widths clear aligns cols: (length? options) / 3 p/pane/1/cols: cols p/pane/1/data: p/data col-offset: total-width: 0 foreach [column halign width] options [ unless any [string? column word? column] [ gui-error "Table expected column name to be a string or word" ] unless find [left center right] halign [ gui-error "Table expected column align to be one of left, center or right" ] unless decimal? width [ gui-error "Table expected column width to be a decimal" ] insert tail aligns halign insert tail widths width: to integer! p/size/x * width total-width: total-width + width insert back tail pane make subface [ offset: as-pair col-offset 0 size: as-pair width - sizes/cell sizes/line text: form column color: colors/theme-dark col: length? widths para: make default-para [margin: as-pair sizes/line + 2 2] ;BEG fixed by Cyphre, sponsored by Robert font: make default-font-heading [align: aligns/:col] ;END fixed by Cyphre, sponsored by Robert feel: make default-feel [ over: make function! [face act pos] [ face/color: either act [colors/theme-light] [colors/theme-dark] show face ] engage: make function! [face act event /local arrow] [ if act = 'down [ arrow: last parent-face/pane unless arrow/col = col [ arrow/col: col arrow/asc: none arrow/offset/x: offset/x + size/x - (sizes/cell * 3) ] arrow/action arrow ] ] ] ] col-offset: col-offset + width ; resize dragger if cols > length? widths [ insert back tail pane make subface [ offset: as-pair col-offset - sizes/cell 0 ;BEG fixed by Cyphre, sponsored by Robert size: as-pair 2 either no-dividers [sizes/line] [p/size/y] ;END fixed by Cyphre, sponsored by Robert color: colors/outline-dark span: unless no-dividers [all [p/span find p/span #H #H]] ; AGT resize bars if needed col-1: length? widths col-2: 1 + length? widths feel: make default-feel [ over: make function! [face act pos] [ color: either act [colors/state-dark] [colors/outline-dark] show face ] engage: make function! [face act event /local delta arrow] [ switch/default act [ down [data: event/offset/x] up [data: none feel/over face false 0x0] alt-up [data: none feel/over face false 0x0] ][ if all [ data event/type = 'move event/offset/x <> data ] [ delta: event/offset/x - data delta: either positive? delta [ min delta parent-face/pane/(col-2 * 2)/size/x - (sizes/line * 2) ][ max delta negate parent-face/pane/(col-1 * 2)/size/x - (sizes/line * 2) ] unless zero? delta [ arrow: last parent-face/pane if arrow/col = col-1 [arrow/offset/x: arrow/offset/x + delta] ; move dragger bar offset/x: offset/x + delta ; adjust column widths widths/:col-1: widths/:col-1 + delta widths/:col-2: widths/:col-2 - delta ; adjust heading widths and offset parent-face/pane/(col-1 * 2)/size/x: widths/:col-1 - sizes/cell parent-face/pane/(col-2 * 2)/offset/x: offset/x + sizes/cell either cols = col-2 [ parent-face/pane/(col-2 * 2)/size/x: widths/:col-2 ][ parent-face/pane/(col-2 * 2)/size/x: widths/:col-2 - sizes/cell ] ; show changes show parent-face ] ] ] ] ] ] ] ] ; reassign options p/options: pane/1/options ; is total-width OK last-col: first back back tail pane last-col/size/x: last-col/size/x + sizes/cell + size/x - total-width if negative? last-col/size/x [ gui-error "Table column widths are too large" ] widths/:cols: widths/:cols + size/x - total-width ; is last label resizeable? if all [span find span #W] [ last-col/span: #W ] ; init iterator *after* we know align pane/1/init unless no-show [ show self ] ] ;END fixed by Cyphre, sponsored by Robert init: make function! [/local p opts dividers?] [ ; default options opts: [table] if 'multi = first options [remove options insert tail opts 'multi] dividers?: either 'no-dividers = first options [remove options false] [true] if 'multi = first options [remove options insert tail opts 'multi] ; basic options and data validation unless integer? cols: divide length? options 3 [ gui-error "Table has an invalid options block" ] if all [not empty? data decimal? divide length? data cols] [ gui-error "Table has an invalid data block" ] ; face iterator p: self insert tail pane make face-iterator [ offset: as-pair 0 sizes/line size: p/size - as-pair 0 sizes/line span: either p/span [copy p/span] [none] data: p/data cols: p/cols widths: p/widths ; share widths aligns: p/aligns ; share aligns options: opts picked: p/picked ; share picked block action: get in p/action 'on-click ; share action func alt-action: get in p/action 'on-alt-click ; share alt-action func dbl-action: get in p/action 'on-dbl-click ; share dbl-action func ] ;BEG fixed by Cyphre, sponsored by Robert ; arrow insert tail pane make subface [ offset: as-pair negate sizes/line sizes/cell size: as-pair sizes/cell * 3 sizes/cell * 3 effect: [arrow black rotate 0] cols: p/cols col: none asc: true feel: make default-feel [ engage: make function! [face act event] [ all [act = 'down face/action face] ] ] action: make function! [face /local last-selected] [ asc: either none? asc [true] [complement asc] effect/rotate: either asc [0] [180] ;BEG fixed by Cyphre, sponsored by Robert last-selected: selected ;END fixed by Cyphre, sponsored by Robert either asc [ sort/skip/compare parent-face/data cols col ][ sort/skip/compare/reverse parent-face/data cols col ] ;BEG fixed by Cyphre, sponsored by Robert all [ last-selected select-row/no-action (((index? find parent-face/data last-selected) - 1) / cols) + 1 ] ;END fixed by Cyphre, sponsored by Robert show parent-face ] ] ; column headings either dividers? [set-columns/no-show options] [set-columns/no-show/no-dividers options] ;END fixed by Cyphre, sponsored by Robert ; accessors redraw: get in pane/1 'redraw selected: get in pane/1 'selected ; feel feel: make default-feel [ redraw: make function! [face act pos /local total arrow] [ if act = 'show [ total: 0 foreach width widths [total: total + width] widths/:cols: widths/:cols + size/x - total ; is arrow on last col arrow: last pane if arrow/col = cols [arrow/offset/x: size/x + sizes/cell - sizes/line] ] ] ] ] ]