PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/source/styles/list.r

http://github.com/henrikmk/VID-Extension-Kit
R | 1173 lines | 1147 code | 23 blank | 3 comment | 93 complexity | 8ea2965987431a9ef2e7ee42612a15c5 MD5 | raw file
  1. REBOL [
  2. Title: "Lists"
  3. Short: "Lists"
  4. Author: ["Henrik Mikael Kristensen"]
  5. Copyright: "2009, 2012 - HMK Design"
  6. Filename: %list.r
  7. Version: 0.0.1
  8. Type: 'script
  9. Maturity: 'unstable
  10. Release: 'internal
  11. Created: 04-Apr-2009
  12. Date: 10-Feb-2012
  13. License: {
  14. BSD (www.opensource.org/licenses/bsd-license.php)
  15. Use at your own risk.
  16. }
  17. Purpose: {}
  18. History: []
  19. Keywords: []
  20. ]
  21. stylize/master [
  22. ; Prototypes for iterated face styles
  23. ITERATED-FACE: BLANK-FACE fill 1x0 spring [bottom]
  24. ITERATED-TEXT: TEXT fill 1x0 spring [bottom]
  25. ITERATED-TXT: TXT fill 1x0 spring [bottom]
  26. ; text styles are modified in-place to accommodate the list, so we don't really have them here
  27. ; generic cell for LIST
  28. LIST-CELL: TEXT with [
  29. text: none
  30. data: none
  31. row: none
  32. size: 0x20
  33. font: make font [valign: 'middle]
  34. para: make para [wrap?: false]
  35. pos: 0x0
  36. name: none
  37. feel: make face/feel [
  38. over: func [face act pos] [
  39. face/parent-face/parent-face/over: all [act face/pos]
  40. show face
  41. ]
  42. engage: func [face act event /local lst pos] [
  43. lst: face/parent-face/parent-face
  44. if act = 'down [
  45. if all [
  46. in lst 'select-func
  47. any-function? get in lst 'select-func
  48. face/pos/y <= length? lst/data-filtered ; do not run for cells that are outside the list filter
  49. ] [
  50. pos: face/pos
  51. lst/select-func face event
  52. act-face
  53. lst
  54. event
  55. pick [on-double-click on-click] event/double-click
  56. face/pos: pos ; maintain position even after list is closed
  57. ]
  58. ]
  59. ]
  60. ]
  61. ]
  62. ; cell text for LIST
  63. LIST-TEXT-CELL: LIST-CELL
  64. ; editable cell text for LIST
  65. LIST-EDIT-CELL: LIST-CELL with [
  66. ; a new feel for this type of text editing
  67. ; need to doubleclick to bring up the cursor
  68. ]
  69. ; cell text for LIST with offset and icon for tree fold/unfold
  70. LIST-TREE-CELL: LIST-CELL with [
  71. level: 1
  72. node-type: 'data
  73. ; [o] - specify level
  74. ; [ ] - specify folder/data
  75. ; [ ] - specify fold action
  76. ; [ ] - specify unfold action
  77. ; [ ] - specify fold icon
  78. ; [ ] - specify unfold icon
  79. ]
  80. ; cell image for LIST
  81. LIST-IMAGE-CELL: IMAGE with [
  82. text: none
  83. data: none
  84. row: none
  85. size: 0x20 ; the size of the cell with a particular standard height
  86. name: none
  87. pos: 0x0
  88. feel: get in get-style 'list-cell 'feel
  89. ]
  90. ; column resizer for LIST
  91. LIST-RESIZER: RESIZER with [
  92. ; needs an ON-DRAG that works on other ON-DRAG elements
  93. ; also needs a different surface, so the list face won't fail here
  94. ; 2 pixels wide
  95. ; no drag bar necessary in sub-face, possibly
  96. ; the drag bar needs to account for the face that it needs to redrag inside the on-drag item
  97. ;
  98. resize-column: func [face] [
  99. ]
  100. append init [
  101. insert-actor-func self 'on-drag :resize-column
  102. ]
  103. ]
  104. ; iterated list with user defined sub-face. internal use only.
  105. LIST: IMAGE with [
  106. ;-- Faces
  107. sub-face: ; face that is used to iterate through the list view
  108. pane: ; iterated sub-face here
  109. v-scroller: ; vertical scroller attached to list face
  110. h-scroller: ; horizontal scroller attached to list face
  111. selected: ; block of integers with selected indexes in the original data
  112. highlighted: ; block of integers with highlighted indexes in the original data
  113. ;-- CTX-LIST information
  114. filter-func: ; filter function
  115. sort-direction: ; 'asc, 'ascending or 'desc, 'descending
  116. sort-column: ; word name of column to sort by
  117. default-sort-direction: ; the direction to sort in, when resetting sorting (ASC, ASCENDING or DESC, DESCENDING) (word)
  118. default-sort-column: ; the column to sort on, when resetting sorting or NONE, if DATA-LIST should use the input sorting (word)
  119. ;-- Data source information
  120. prototype: ; row prototype
  121. data: ; source data list, always starts at header
  122. data-sorted: ; source as sorted indexes (block of integers)
  123. data-filtered: ; source as sorted and filtered indexes (block of integers)
  124. data-display: ; indexes of column positions (block of integers)
  125. columns: ; description of columns (block of words)
  126. column-order: ; block of words describing column output order (block of words)
  127. output: ; output to list from source. index position describes first visible entry.
  128. none
  129. ;-- Settings
  130. follow-size: ; whether to move a PAGE or a LINE, when following the selected row
  131. none
  132. ;-- Layout information
  133. spacing: 0 ; spacing between rows in pixels
  134. over: ; face position currently hovering over
  135. spring: none
  136. feel: make feel [
  137. redraw: func [face act pos][
  138. if all [not svv/resizing? act = 'draw] [
  139. act-face face none 'on-redraw
  140. ]
  141. ]
  142. ]
  143. ;-- Sub-face creation function
  144. make-sub-face: func [face lo /init /local fs] [
  145. fs: face/sub-face: layout/parent/origin lo iterated-face 0x0; copy face/styles ; this only works during init
  146. fs/parent-face: face
  147. fs/size/y: fs/size/y - face/spacing
  148. if face/size [fs/size/x: face/size/x]
  149. set-parent-faces/parent fs face
  150. unless init [
  151. ctx-resize/align/no-show fs
  152. ]
  153. ]
  154. ; determine number of visible rows in list
  155. list-size: func [face] [
  156. to integer! face/size/y - (any [attempt [2 * face/edge/size/y] 0]) / face/sub-face/size/y
  157. ]
  158. ; moves to the given position in the list and makes it visible, if not visible
  159. follow: func [face pos /local idx range size list-size] [
  160. any [pos exit]
  161. list-size: face/list-size face
  162. range: sort reduce [
  163. index? face/output
  164. min length? face/data subtract add index? face/output size: list-size 1
  165. ]
  166. case [
  167. all [pos >= range/1 pos <= range/2] [exit]
  168. pos < range/1 [face/output: at head face/output pos]
  169. pos >= range/2 [face/output: at head face/output pos - size + 1]
  170. ]
  171. ; adjust new position by follow size
  172. if face/follow-size = 'page [
  173. case [
  174. ; if cursor is now at top, move page size back
  175. equal? pos index? face/output [
  176. face/output: at face/output negate (list-size - 1)
  177. ]
  178. ; if cursor is now at bottom, move page size forward
  179. equal? pos - size + 1 index? face/output [
  180. face/output: at face/output list-size
  181. ]
  182. ]
  183. access/clamp-list face
  184. ]
  185. ]
  186. ; calculates list size ratio (internal)
  187. calc-ratio: func [face] [
  188. divide face/list-size face max 1 length? head face/output
  189. ]
  190. ; calculates the list position (internal)
  191. calc-pos: func [face] [
  192. divide subtract index? face/output 1 max 1 (length? head face/output) - face/list-size face
  193. ]
  194. ; maps the face type to the sub-faces through setup types
  195. map-type: func [type] [
  196. any [
  197. select [
  198. number! list-text-cell
  199. string! list-text-cell
  200. image! list-image-cell
  201. ] to word! type
  202. 'list-text-cell ; default
  203. ]
  204. ]
  205. ;-- Pane rendering function
  206. pane-func: func [face [object!] id [integer! pair!] /local count fs spane sz] [
  207. fs: face/sub-face id
  208. if pair? id [return 1 + second id / fs/size]
  209. fs/offset: fs/old-offset: id - 1 * fs/size * 0x1
  210. sz: size/y - any [attempt [2 * face/edge/size/y] 0]
  211. if fs/offset/y > sz [return none]
  212. count: 0
  213. foreach item fs/pane [
  214. if object? item [
  215. face/cell-func
  216. face ; list face
  217. item ; cell face
  218. id ; physical row
  219. count: count + 1 ; phyiscal column
  220. fs/offset/y + fs/size/y <= sz ; render or not
  221. ]
  222. ]
  223. fs
  224. ]
  225. ;-- Cell content function
  226. cell-func: func [face cell row col render /local fp inside r] [
  227. cell/pos: as-pair col row - 1 + index? face/output
  228. cell/name: pick face/column-order col
  229. r: all [
  230. render
  231. inside: row <= length? face/output
  232. pick pick face/output row col
  233. ]
  234. cell/access/set-face* cell r
  235. ; this produces selected rows outside, which means that selected rows will appear at the bottom
  236. ; with no text in them
  237. back-render-func face cell
  238. either inside [
  239. cell/row: pick face/data pick face/data-sorted cell/pos/y
  240. render-func face cell
  241. ][
  242. empty-render-func face cell
  243. ]
  244. ]
  245. ;-- Cell background render function (could be optimized, if we had disable-face*)
  246. back-render-func: func [face cell /local colors y-pos] [
  247. colors: ctx-colors/colors
  248. y-pos: pick face/data-sorted cell/pos/y
  249. case [
  250. find face/highlighted y-pos [
  251. cell/color:
  252. either flag-face? face disabled [
  253. colors/select-disabled-color
  254. ][
  255. colors/field-select-color
  256. ]
  257. cell/font/color:
  258. either flag-face? face disabled [
  259. colors/body-text-disabled-color
  260. ][
  261. colors/body-text-color
  262. ]
  263. ]
  264. find face/selected y-pos [
  265. cell/color:
  266. either flag-face? face disabled [
  267. colors/select-disabled-color
  268. ][
  269. colors/select-color
  270. ]
  271. cell/font/color:
  272. colors/select-body-text-color
  273. ]
  274. true [
  275. cell/color:
  276. colors/field-color
  277. cell/font/color:
  278. either flag-face? face disabled [
  279. colors/body-text-disabled-color
  280. ][
  281. colors/body-text-color
  282. ]
  283. ]
  284. ]
  285. if odd? cell/pos/y [
  286. cell/color: cell/color - 10
  287. ]
  288. ]
  289. ;-- Cell foreground render function
  290. render-func: none
  291. ;-- Empty Cell foreground render function
  292. empty-render-func: none
  293. ;-- Cell selection function for mouse. FACE is cell that is clicked on.
  294. select-mode: 'multi
  295. start: end: none
  296. select-func: func [face event /local old s step] [
  297. old: copy selected
  298. lst: face/parent-face/parent-face
  299. switch select-mode [
  300. mutex [
  301. ;-- Single selection
  302. append clear selected pick lst/data-sorted start: end: face/pos/y
  303. ]
  304. multi [
  305. ;-- Select multiple rows
  306. case [
  307. event/shift [
  308. either start [
  309. step: pick [1 -1] start < end
  310. for i start end step [remove find selected i]
  311. step: pick [1 -1] start < end: face/pos/y
  312. for i start face/pos/y step [
  313. append selected pick lst/data-sorted i
  314. ]
  315. ][
  316. append selected pick lst/data-sorted start: end: face/pos/y
  317. ]
  318. ]
  319. event/control [
  320. alter selected pick lst/data-sorted start: end: face/pos/y
  321. ]
  322. true [
  323. append clear selected pick lst/data-sorted start: end: face/pos/y
  324. ]
  325. ]
  326. ]
  327. persistent [
  328. ;-- Selection stays
  329. alter selected pick lst/data-sorted start: end: face/pos/y
  330. ]
  331. ]
  332. sel: copy selected
  333. selected: head insert head clear selected unique sel
  334. if sel <> old [
  335. old: face/pos
  336. do-face self selected
  337. show self
  338. face/pos: old ; otherwise it changes due to SHOW
  339. ]
  340. act-face self none 'on-select
  341. ]
  342. ;-- Cell selection function for keyboard. FACE is the list in focus.
  343. key-select-func: func [face event /local dir keys old out s step] [
  344. case [
  345. #"^A" = event/key [
  346. select-face/no-show face not event/shift
  347. ]
  348. #"^M" = event/key [
  349. act-face face none 'on-return
  350. ]
  351. #"^[" = event/key [
  352. act-face face none 'on-escape
  353. ]
  354. keys: find [up down] event/key [
  355. old: copy face/selected
  356. out: head face/output
  357. dir: pick [1 -1] event/key = 'down
  358. if event/control [dir: dir * list-size face]
  359. if empty? out [clear face/selected return false]
  360. either empty? face/selected [
  361. append face/selected pick face/data-sorted face/start: face/end: 1
  362. ][
  363. case [
  364. all [select-mode <> 'mutex event/shift] [
  365. step: pick [1 -1] face/start < face/end
  366. for i face/start face/end step [remove find face/selected pick face/data-sorted i]
  367. step: pick [1 -1] face/start < (face/end: face/end + dir)
  368. face/end: max 1 min length? out face/end
  369. for i face/start face/end step [insert tail face/selected pick face/data-sorted i]
  370. ]
  371. true [
  372. either face/start [
  373. append clear face/selected pick face/data-sorted face/start: face/end + dir
  374. ][
  375. face/start: 1
  376. ]
  377. face/start: face/end: max 1 min length? out face/start
  378. face/selected/1: pick face/data-sorted face/start
  379. ]
  380. ]
  381. ]
  382. follow face face/end
  383. ]
  384. ]
  385. sel: copy face/selected
  386. face/selected: head insert head clear face/selected unique sel
  387. if sel <> old [
  388. do-face self get-face face
  389. if keys [act-face face none 'on-select]
  390. ]
  391. event
  392. ]
  393. ;-- Accessor functions
  394. access: make access [
  395. ; makes sure the list output does not scroll beyond the edge
  396. clamp-list: func [face] [
  397. face/output:
  398. at
  399. head face/output
  400. min
  401. index? face/output
  402. index? at tail face/output negate face/list-size face
  403. ]
  404. ; performs face navigation using LIST-* styles in the sub-face
  405. key-face*: func [face event /local old] [
  406. event: face/key-select-func face event
  407. act-face face event 'on-key
  408. event
  409. ]
  410. ; scrolls the list face
  411. scroll-face*: func [face x y /local old size] [
  412. old: face/output
  413. size: face/list-size face
  414. face/output:
  415. either 1 < abs y [ ; OSX sends only 1 step instead of 3
  416. ;-- Scroll wheel
  417. skip face/output pick [1 -1] positive? y
  418. ][
  419. ;-- Scroll bar
  420. at head face/output add y * subtract length? face/data size 1
  421. ]
  422. clamp-list face
  423. not-equal? index? old index? face/output ; update only for show when the index shows a difference
  424. ]
  425. ; returns selected rows from the list face
  426. get-face*: func [face /local vals] [
  427. case [
  428. none? face/selected [none]
  429. face/select-mode = 'mutex [
  430. unless empty? face/selected [pick head face/data first face/selected]
  431. ]
  432. empty? face/selected [make block! []]
  433. true [
  434. vals: make block! length? face/selected
  435. foreach pos face/selected [
  436. append/only vals pick head face/data pos
  437. ]
  438. vals
  439. ]
  440. ]
  441. ]
  442. ; returns selection indexes from the list face
  443. get-select-face*: func [face] [
  444. face/selected
  445. ]
  446. ; resizes the sub-face of the list
  447. resize-face*: func [face size x y] [
  448. ;-- Resize main list face and sub-face
  449. resize/no-show
  450. face/sub-face
  451. as-pair
  452. face/size/x
  453. face/sub-face/size/y
  454. face/sub-face/offset
  455. ;-- Clamp list if it's beyond end
  456. clamp-list face
  457. ]
  458. ; sets the content of face data and re-filters and re-sorts the list
  459. set-face*: func [face data] [
  460. clear face/selected
  461. if object? data [
  462. data: ctx-list/object-to-data data
  463. ]
  464. face/data: any [data make block! []]
  465. refresh-face/no-show face
  466. act-face face none 'on-unselect
  467. ]
  468. ; clears the face data block
  469. clear-face*: func [face] [
  470. clear face/selected
  471. clear face/data
  472. refresh-face/no-show face
  473. act-face face none 'on-unselect
  474. ]
  475. ; selects rows in the face
  476. select-face*: func [face values /local old-selection new-value] [
  477. old-selection: copy face/selected
  478. clear face/selected
  479. case [
  480. ;-- Select Nothing
  481. empty? face/data-sorted [
  482. act-face face none 'on-unselect
  483. ]
  484. ;-- Select Range
  485. any [integer? :values any-block? :values] [
  486. insert face/selected unique intersect to block! values face/data-sorted
  487. act-face face none 'on-select
  488. ]
  489. ;-- Select by Function
  490. any-function? :values [
  491. foreach id face/data-sorted [
  492. if values pick face/data id [insert tail face/selected id]
  493. ]
  494. act-face
  495. face
  496. none
  497. either empty? face/selected ['on-unselect]['on-select]
  498. ]
  499. ;-- Invert Selection
  500. 'invert = :values [
  501. switch face/select-mode [
  502. mutex [
  503. either empty? face/selected [
  504. insert face/selected first face/data-sorted
  505. ][
  506. clear face/selected
  507. ]
  508. ]
  509. multi persistent [
  510. insert face/selected exclude face/data-sorted old-selection
  511. ]
  512. ]
  513. act-face face none 'on-select
  514. ]
  515. ;-- Select First
  516. 'first = :values [
  517. insert face/selected first face/data-sorted
  518. follow face 1
  519. act-face face none 'on-select
  520. ]
  521. ;-- Select Next
  522. 'next = :values [
  523. new-value: find face/data-sorted old-selection/1
  524. new-value: either new-value [
  525. at head new-value min index? next new-value index? back tail new-value
  526. ][
  527. face/data-sorted
  528. ]
  529. insert face/selected first new-value
  530. follow face index? new-value
  531. act-face face none 'on-select
  532. ]
  533. ;-- Select Previous
  534. 'previous = :values [
  535. new-value: find face/data-sorted old-selection/1
  536. new-value: either new-value [back new-value][face/data-sorted]
  537. insert face/selected first new-value
  538. follow face index? new-value
  539. act-face face none 'on-select
  540. ]
  541. ;-- Select Last
  542. 'last = :values [
  543. insert face/selected last face/data-sorted
  544. follow face length? face/data-sorted
  545. act-face face none 'on-select
  546. ]
  547. ;-- Select All
  548. true = :values [
  549. switch face/select-mode [
  550. mutex [
  551. insert face/selected first face/data-sorted
  552. ]
  553. multi persistent [
  554. insert face/selected face/data-sorted
  555. ]
  556. ]
  557. act-face face none 'on-select
  558. ]
  559. ]
  560. face/start: face/selected/1
  561. face/end: all [not empty? face/selected last face/selected]
  562. ]
  563. ; unselects rows in the face
  564. unselect-face*: func [face values /local old-selection] [
  565. old-selection: copy face/selected
  566. if any [integer? :values any-block? :values] [
  567. insert clear face/selected exclude old-selection to block! :values
  568. act-face face none 'on-unselect
  569. ]
  570. ]
  571. ; performs filtering of rows in the list
  572. query-face*: func [face value] [
  573. clear face/selected
  574. face/filter-func: :value
  575. refresh-face/no-show face
  576. act-face face none 'on-unselect
  577. ]
  578. ; perform edits on the list, when the list is object based
  579. edit-face*: func [face op value pos word /local blk j old-sorted] [
  580. pos:
  581. switch/default pos [
  582. last [length? face/data]
  583. first [1]
  584. all [blk: make block! length? face/data repeat i length? face/data [append blk i] blk]
  585. ][
  586. face/selected
  587. ]
  588. switch :op [
  589. add [
  590. old-sorted: copy face/data-sorted
  591. foreach
  592. val
  593. case [
  594. object? :value [
  595. reduce [:value]
  596. ]
  597. block? :value [
  598. :value
  599. ]
  600. none? :value [
  601. []
  602. ]
  603. ]
  604. [
  605. append/only face/data make face/prototype val
  606. ]
  607. refresh-face/no-show face
  608. select-face face exclude face/data-sorted old-sorted
  609. ]
  610. duplicate [
  611. j: length? face/data
  612. repeat i length? pos [
  613. append/only face/data make face/prototype pick face/data pick pos i
  614. ]
  615. refresh-face/no-show face
  616. select-face face array/initial length? pos does [j: j + 1]
  617. ]
  618. edit update [
  619. repeat i length? pos [
  620. change at face/data pick pos i make pick face/data pick pos i :value
  621. ]
  622. refresh-face/no-show face
  623. ]
  624. delete remove [
  625. repeat i length? pos [change at face/data pick pos i ()]
  626. remove-each row face/data [not value? 'row]
  627. clear pos
  628. select-face/no-show face none
  629. refresh-face/no-show face
  630. ]
  631. ]
  632. ]
  633. refresh-face*: func [face] [
  634. ctx-list/set-filtered face
  635. act-face face none 'on-refresh
  636. ]
  637. ]
  638. init: [
  639. ; Set up columns
  640. any [columns columns: [column]]
  641. all [columns not column-order column-order: columns]
  642. ; Build sub-face
  643. make-sub-face/init self any [sub-face [list-text-cell]] ; empty sub-face = infinite loop
  644. if none? size [
  645. size: 300x200
  646. size/x: sub-face/size/x + first edge-size? self
  647. ]
  648. ; Attach source data
  649. if none? data [data: make block! []]
  650. if object? data [data: ctx-list/object-to-data data]
  651. output: copy data-sorted: copy data-filtered: copy data-display: make block! length? data
  652. refresh-face/no-show self
  653. ; Prepare selection
  654. any [block? selected selected: make block! []]
  655. any [block? highlighted highlighted: make block! []]
  656. ; Prepare rendering
  657. pane: :pane-func
  658. ]
  659. ]
  660. ; list with keyboard caret selection instead of plain selection
  661. CARET-LIST: LIST with [
  662. ;-- Cell selection function for keyboard. FACE is the list that holds the caret.
  663. key-select-func: func [face event /local dir out s step] [
  664. if find [up down] event/key [
  665. out: head output
  666. dir: pick [1 -1] event/key = 'down
  667. if event/control [dir: dir * list-size face]
  668. if empty? out [
  669. face/over: none
  670. clear face/highlighted
  671. act-face face none 'on-unselect
  672. return false
  673. ]
  674. either empty? face/highlighted [
  675. insert
  676. face/highlighted
  677. first either empty? face/selected [
  678. face/data-sorted
  679. ][
  680. face/selected
  681. ]
  682. ][
  683. all [
  684. s: find face/data-sorted face/highlighted/1
  685. s: skip s dir
  686. change face/highlighted first either tail? s [back s][s]
  687. ]
  688. ]
  689. follow face first face/highlighted
  690. act-face face none 'on-highlight
  691. ]
  692. if find [#" " #"^M"] event/key [
  693. unless empty? face/highlighted [
  694. append clear face/selected face/highlighted
  695. ]
  696. if event/key = #"^M" [
  697. act-face face none 'on-return
  698. ]
  699. act-face face none 'on-select
  700. ]
  701. event
  702. ]
  703. ]
  704. ; CARET-LIST used in CHOICE selector
  705. CHOICE-LIST: CARET-LIST fill 1x1 with [
  706. access: make access [
  707. ; moves the window face to within screen-face dimensions
  708. scroll-face*: func [face x y /local dir menu-face opener-face window-face] [
  709. dir: pick [-1 1] positive? y
  710. opener-face: get-opener-face
  711. window-face: find-window opener-face
  712. menu-face: get-menu-face
  713. menu-face/offset/y: opener-face/size/y * dir + menu-face/offset/y
  714. ; Fix pixel offset error for negative values
  715. ; (not a perfect fix, as there is still a one-pixel but constant offset)
  716. if menu-face/offset/y < 0 [menu-face/offset/y: menu-face/offset/y - 1]
  717. ; Restrain to opener-face top
  718. menu-face/offset/y:
  719. min
  720. menu-face/offset/y
  721. window-face/offset/y + opener-face/win-offset/y
  722. ; Restrain to opener-face bottom
  723. menu-face/offset/y:
  724. max
  725. menu-face/offset/y
  726. add
  727. window-face/offset/y
  728. opener-face/win-offset/y + opener-face/size/y - menu-face/size/y
  729. show menu-face
  730. false
  731. ]
  732. ]
  733. ]
  734. ; iterated bottom-up list with user defined sub-face. internal use only.
  735. REVERSE-LIST: LIST with [
  736. ; determine which changes are needed here:
  737. ; output is reversed, so we need to output this somehow reversed, possibly pane-func ID
  738. ; scroller should not be affected, as it behaves like normal, but is clamped to the bottom
  739. ; the filter functions should not be needed, but may be possible to do
  740. ]
  741. ; panel that serves a list and navigation faces. internal.
  742. NAV-LIST: PANEL with [
  743. ;-- Faces
  744. list: ; list face
  745. pane: ; layed out list and navigation faces
  746. ;-- List information
  747. data: ; data block to use as source, passed to LIST
  748. columns: ; column description, passed to LIST
  749. column-order: ; column order, passed to LIST
  750. sub-face: ; sub-face block or layout, passed to LIST
  751. back-render: ; background cell render function body
  752. empty-render: ; empty row render function body
  753. render: ; cell render function body
  754. text: ; does not contain focusable text
  755. none
  756. ;-- Basic accessors
  757. access: make access [
  758. get-face*: func [face] [
  759. if face/list [get-face face/list]
  760. ]
  761. ]
  762. ]
  763. ; standard data list with list, user defined header and user defined sub-face or column definition
  764. DATA-LIST: NAV-LIST with [ ; [!] - compound later
  765. ; surface: 'static-frame
  766. ;-- Faces
  767. header-face: ; header face
  768. v-scroller: ; vertical scroller face
  769. h-scroller: ; horizontal scroller face
  770. none
  771. ; [?] - do not allow focusing of individual items in list sub-face
  772. ; [ ] - inline field system, realized by having text styles that do inline editing
  773. ;-- Specification
  774. prototype: ; prototype for list row (object)
  775. input: ; input words for data source (block of words)
  776. output: ; output words for data display (block of words)
  777. select-mode: ; selection mode (MULTI, MUTEX, PERSISTENT) (word)
  778. widths: ; widths of columns in pixels (block of integers)
  779. adjust: ; LEFT, RIGHT or CENTER adjustment of column texts (block of words)
  780. modes: ; Column modes (SORT, NO-SORT, FILTER)
  781. types: ; Column datatypes (block of datatypes)
  782. names: ; Column names (block of strings)
  783. selected: ; selected rows in list (block of integers)
  784. resize-column: ; which single column resizes (word)
  785. default-sort-direction: ; the direction to sort in, when resetting sorting (ASC, ASCENDING or DESC, DESCENDING) (word)
  786. default-sort-column: ; the column to sort on, when resetting sorting or NONE, if DATA-LIST should use the input sorting (word)
  787. follow-size: ; whether to move a PAGE or a LINE, when following the selected row
  788. none
  789. access: make access [
  790. ; adjusts the scroller ratio and drag (internal)
  791. set-scroller: func [face /only] [
  792. ; this is done on each scroll
  793. face/v-scroller/ratio: face/list/calc-ratio face/list face/v-scroller
  794. face/v-scroller/redrag face/v-scroller/ratio
  795. any [only set-face/no-show face/v-scroller face/list/calc-pos face/list]
  796. ]
  797. ; checks if the output is scrolled past end
  798. past-end?: func [face] [
  799. all [
  800. not head? face/list/output ; not at beginning
  801. greater?
  802. face/list/list-size face/list
  803. length? face/list/output ; is past end
  804. ]
  805. ]
  806. key-face*: func [face event] [
  807. event: face/list/access/key-face* face/list event
  808. set-scroller face
  809. event
  810. ]
  811. set-face*: func [face data] [
  812. face/list/access/set-face* face/list data
  813. face/data: face/list/data
  814. set-scroller face
  815. ]
  816. get-face*: func [face] [
  817. face/list/access/get-face* face/list
  818. ]
  819. get-select-face*: func [face] [
  820. face/list/access/get-select-face* face/list
  821. ]
  822. clear-face*: func [face] [
  823. face/list/access/clear-face* face/list data
  824. face/data: face/list/data
  825. set-scroller face
  826. ]
  827. scroll-face*: func [face x y] [
  828. also
  829. face/list/access/scroll-face* face/list x y
  830. set-scroller face
  831. ]
  832. setup-face*: func [face values /local output-length] [
  833. ;-- Reset face values
  834. if object? values [values: reduce ['input words-of values]]
  835. foreach
  836. word
  837. [input output widths adjust modes types names resize-column header-face sub-face render]
  838. [set in face word none]
  839. foreach
  840. [word value]
  841. any [values []]
  842. [
  843. value:
  844. case [
  845. lit-word? :value [:value]
  846. word? :value [get :value]
  847. path? :value [either value? :value [do :value][:value]]
  848. object? :value [words-of :value]
  849. true [:value]
  850. ]
  851. set in face word value
  852. ]
  853. ;-- Convert Input and Output, if they are objects
  854. if object? face/input [face/input: words-of face/input]
  855. if object? face/output [face/output: words-of face/output]
  856. ;-- Determine prototype from input, output, first row of data or default in that order
  857. face/prototype:
  858. case [
  859. block? face/input [ctx-list/make-list-object/words length? face/input face/input]
  860. block? face/output [ctx-list/make-list-object/words length? face/output face/output]
  861. not block? face/data [ctx-list/make-list-object 1]
  862. empty? face/data [ctx-list/make-list-object 1]
  863. block? face/data/1 [ctx-list/make-list-object length? face/data/1]
  864. object? face/data/1 [make face/data/1 []]
  865. true [ctx-list/make-list-object 1]
  866. ]
  867. set face/prototype none
  868. ;-- Determine Input and Output
  869. case [
  870. all [face/input not face/output] [face/output: copy face/input]
  871. all [not face/input face/output] [face/input: copy face/output]
  872. not all [face/input face/output] [face/output: copy face/input: words-of face/prototype]
  873. ]
  874. ;-- Determine Output Length
  875. face/column-order: copy face/output
  876. remove-each val face/column-order [find [| ||] val]
  877. output-length: length? face/column-order
  878. ;-- Names
  879. if none? face/names [
  880. face/names: make block! length? face/output
  881. foreach word face/column-order [
  882. append face/names uppercase/part form word 1
  883. ]
  884. ]
  885. ;-- Select Mode
  886. if none? face/select-mode [
  887. face/select-mode: 'multi
  888. ]
  889. ;-- Default Sort Direction
  890. if none? face/default-sort-direction [
  891. face/default-sort-direction: 'ascending
  892. ]
  893. ;-- Follow Size
  894. if none? face/follow-size [
  895. face/follow-size: 'line
  896. ]
  897. ;-- Widths
  898. if none? face/widths [
  899. face/widths: array/initial output-length 100
  900. ]
  901. ;-- Adjustment
  902. if none? face/adjust [
  903. face/adjust: array/initial output-length 'left
  904. ]
  905. ;-- Types
  906. if none? face/types [
  907. face/types: array/initial output-length string!
  908. ]
  909. ;-- Modes
  910. if none? face/modes [
  911. face/modes: array/initial output-length 'sort
  912. ]
  913. ;-- Resize column
  914. if none? face/resize-column [
  915. face/resize-column: first face/column-order
  916. ]
  917. ;-- Header Face
  918. if none? face/header-face [
  919. ctx-list/make-header-face face
  920. ]
  921. ;-- Sub Face
  922. if none? face/sub-face [
  923. ctx-list/make-sub-face face
  924. ]
  925. ;-- Arrange Layout
  926. face/pane: copy [across space 0]
  927. ;-- Build Header
  928. if face/header-face [
  929. append face/pane compose/only [
  930. panel fill 1x0 spring [bottom] (face/header-face) return
  931. ]
  932. ]
  933. ;-- Build Pane
  934. append face/pane [
  935. scroller 20x100 fill 0x1 align [right] [
  936. scroll-face face/parent-face/list 0 get-face face
  937. ]
  938. list fill 1x1 align [left]
  939. [do-face face/parent-face none]
  940. with [ ; size is ignored, because it's made inside list size
  941. sub-face: (face/sub-face)
  942. data: (face/data)
  943. columns: (face/input)
  944. column-order: (face/column-order)
  945. ]
  946. ]
  947. face/pane: layout/tight compose/deep/only face/pane
  948. set-parent-faces face
  949. ;-- Calculate sizes
  950. any [face/size face/size: face/pane/size + any [all [object? face/edge 2 * face/edge/size] 0]]
  951. face/panes: reduce ['default face/pane: face/pane/pane]
  952. ;-- Name faces
  953. set bind either face/header-face [
  954. [header-face v-scroller list]
  955. ][
  956. [v-scroller list]
  957. ] face face/pane
  958. ;-- Sharing
  959. face/data: face/list/data
  960. face/selected: face/list/selected
  961. face/list/prototype: face/prototype
  962. face/list/v-scroller: face/v-scroller
  963. face/list/select-mode: face/select-mode
  964. face/list/follow-size: face/follow-size
  965. face/list/default-sort-direction: face/default-sort-direction
  966. face/list/default-sort-column: face/default-sort-column
  967. if get in face 'back-render [face/list/back-render-func: func [face cell] get in face 'back-render]
  968. if get in face 'empty-render [face/list/empty-render-func: func [face cell] get in face 'empty-render]
  969. if get in face 'render [face/list/render-func: func [face cell] get in face 'render]
  970. ;-- Map actors from DATA-LIST to internal components
  971. foreach actor first face/actors [
  972. if find [
  973. on-click on-key on-select on-unselect on-return on-escape on-double-click
  974. ] actor [
  975. pass-actor face face/list actor
  976. ]
  977. ]
  978. ;-- Sort by default settings, if there is a default sort column
  979. if face/list/default-sort-column [
  980. face/list/sort-direction: face/list/default-sort-direction
  981. face/list/sort-column: face/list/default-sort-column
  982. ]
  983. ;-- Setup Scroller
  984. insert-actor-func face 'on-align get in access 'set-scroller
  985. insert-actor-func face 'on-resize get in access 'set-scroller
  986. ;-- Refresh content
  987. refresh-face/no-show face
  988. ]
  989. select-face*: func [face values] [
  990. face/list/access/select-face* face/list :values
  991. set-scroller face
  992. ]
  993. unselect-face*: func [face values] [
  994. face/list/access/unselect-face* face/list :values
  995. set-scroller face
  996. ]
  997. query-face*: func [face value] [
  998. face/list/access/query-face* face/list :value
  999. set-scroller face
  1000. ]
  1001. edit-face*: func [face op value pos word] [
  1002. face/list/access/edit-face* face/list :op :value :pos :word
  1003. set-scroller face
  1004. ]
  1005. refresh-face*: func [face] [
  1006. face/list/access/refresh-face* face/list
  1007. foreach f face/header-face/pane/pane [
  1008. if all [
  1009. f/style = 'sort-button
  1010. f/feel ; find better way to detect a valid sort button
  1011. ] [
  1012. either f/column = face/list/sort-column [
  1013. set-face f face/list/sort-direction
  1014. ][
  1015. clear-face f
  1016. ]
  1017. ]
  1018. ]
  1019. set-scroller face
  1020. ]
  1021. ]
  1022. ;-- List Functions
  1023. follow: func [face pos] [
  1024. face/list/follow face/list pos
  1025. set-face/no-show face/v-scroller face/list/calc-pos face/list
  1026. ]
  1027. ;-- Dialect Words
  1028. words: [
  1029. data [
  1030. if block? args [new/data: args/2]
  1031. next args
  1032. ]
  1033. ]
  1034. init: [
  1035. access/setup-face* self setup
  1036. access/refresh-face* self
  1037. ]
  1038. ]
  1039. ; single sort button for the header face
  1040. SORT-BUTTON: BUTTON ctx-colors/colors/manipulator-color with [
  1041. column: none ; the name or index position of the column that is to be sorted.
  1042. list: none ; list face to sort
  1043. feel: svvf/mutex
  1044. access: ctx-access/data-state
  1045. states: [no-sort ascending descending]
  1046. virgin: true ; do not repeat the no-sort state
  1047. surface: 'sort
  1048. action: func [face value] [
  1049. any [
  1050. face/list
  1051. face/list: find-style face/parent-face/parent-face 'list
  1052. ]
  1053. face/list/sort-direction: first face/states
  1054. face/list/sort-column: face/column
  1055. ctx-list/set-sorting face/list
  1056. scroll-face/no-show face/list 0 get-face face/list/v-scroller
  1057. svvf/reset-related-faces face/parent-face
  1058. show face/list
  1059. ]
  1060. words: [
  1061. sort-column [
  1062. if block? args [new/column: args/2]
  1063. next args
  1064. ]
  1065. ]
  1066. ]
  1067. ; perform reset sort action on parent list
  1068. SORT-RESET-BUTTON: BUTTON 20x24 ctx-colors/colors/action-color with [
  1069. font: none
  1070. text: none
  1071. list: none ; list face to unsort
  1072. surface: 'sort-reset
  1073. action: func [face value] [
  1074. any [
  1075. face/list
  1076. face/list: find-style face/parent-face/parent-face 'list
  1077. ]
  1078. if any [
  1079. face/list/default-sort-column
  1080. face/list/sort-column
  1081. ] [
  1082. face/list/sort-direction: face/list/default-sort-direction
  1083. face/list/sort-column: face/list/default-sort-column
  1084. foreach f face/parent-face/pane [
  1085. if f <> face [
  1086. either f/column = face/list/sort-column [
  1087. set-face f face/list/sort-direction
  1088. ][
  1089. clear-face f
  1090. ]
  1091. ]
  1092. ]
  1093. ctx-list/set-sorting face/list
  1094. scroll-face/no-show face/list 0 get-face face/list/v-scroller
  1095. show face/list
  1096. ]
  1097. ]
  1098. ]
  1099. ; column filtering button
  1100. FILTER-BUTTON: CHOICE with [
  1101. column: none
  1102. list: none
  1103. choices: [all "<All>" none "<None>" not-empty "<Not Empty>" empty "<Empty>"]
  1104. action: func [face value] [
  1105. ; all
  1106. ; empty
  1107. ; not empty
  1108. ; unique entries
  1109. ]
  1110. ; need to allow changing this every time the contents change
  1111. ; need a method to set this face up, so we might need a new setup-face for this
  1112. ; derive filter-button from choice and then provide a new setup-face
  1113. access: make access [
  1114. setup-face*: func [face value] [
  1115. ; get this from parent
  1116. ; but the values must be fed in here from the outside
  1117. face/setup: value
  1118. if value [
  1119. face/data: copy face/setup
  1120. set-face* face face/data/1
  1121. ]
  1122. ]
  1123. ]
  1124. append init [
  1125. access/setup-face* self choices
  1126. ]
  1127. words: [
  1128. sort-column [
  1129. if block? args [new/column: args/2]
  1130. next args
  1131. ]
  1132. ]
  1133. ]
  1134. TABLE: LIST
  1135. TEXT-LIST: DATA-LIST setup [
  1136. input [items]
  1137. widths [200]
  1138. select-mode 'mutex
  1139. header-face []
  1140. ]
  1141. PARAMETER-LIST: DATA-LIST setup [
  1142. ; need to allow defining bold font
  1143. input [key value]
  1144. output [key | value]
  1145. widths [100 200]
  1146. resize-column 'value
  1147. ]
  1148. ]
  1149. ; Exported styles
  1150. iterated-face: get-style 'iterated-face