| 1 | area: make rebface [
|
|---|
| 2 | tip: {
|
|---|
| 3 | USAGE:
|
|---|
| 4 | area
|
|---|
| 5 | area "Text" -1
|
|---|
| 6 | area "Text" 50x-1
|
|---|
| 7 |
|
|---|
| 8 | DESCRIPTION:
|
|---|
| 9 | Editable text area with wrapping and scroller.
|
|---|
| 10 |
|
|---|
| 11 | OPTIONS:
|
|---|
| 12 | 'info specifies read-only
|
|---|
| 13 | }
|
|---|
| 14 | size: 50x25
|
|---|
| 15 | text: ""
|
|---|
| 16 | color: colors/page
|
|---|
| 17 | edge: theme-edge
|
|---|
| 18 | font: default-font-top
|
|---|
| 19 | para: make default-para-wrap [margin: as-pair sizes/slider + 2 2]
|
|---|
| 20 | feel: make edit/feel [
|
|---|
| 21 | redraw: func [face act pos /local height total visible] [
|
|---|
| 22 | if act = 'show [
|
|---|
| 23 | ; check for size change and resize scroller
|
|---|
| 24 | if face/size <> face/old-size [
|
|---|
| 25 | face/pane/offset/x: max 0 face/size/x - face/pane/size/x
|
|---|
| 26 | face/pane/size/y: face/size/y
|
|---|
| 27 | ]
|
|---|
| 28 | if any [
|
|---|
| 29 | face/text-y <> height: second size-text face ; height of text changed ?
|
|---|
| 30 | face/size <> face/old-size ; size changed ?
|
|---|
| 31 | ][
|
|---|
| 32 | face/text-y: height
|
|---|
| 33 |
|
|---|
| 34 | total: face/text-y
|
|---|
| 35 | visible: face/size/y - (edge/size/y * 2) - para/origin/y - para/indent/y
|
|---|
| 36 |
|
|---|
| 37 | face/pane/ratio: either total > 0 [min 1 (visible / total)][1]
|
|---|
| 38 |
|
|---|
| 39 | ; update scroller step
|
|---|
| 40 | face/pane/step: either visible < total [min 1 (sizes/font-height / (total - visible))][0]
|
|---|
| 41 | ]
|
|---|
| 42 | ; Only update slider/data if scroll was caused by a key (not by slider itself). Avoids recursion.
|
|---|
| 43 | if all [face/pane/ratio < 1 face/key-scroll?] [
|
|---|
| 44 |
|
|---|
| 45 | do bind [
|
|---|
| 46 | ; Update slider dragger position to reflect para/scroll/y
|
|---|
| 47 | ; para/scroll is relative to edge/size + para/origin + (para/indent * 0x1)
|
|---|
| 48 |
|
|---|
| 49 | total: text-y
|
|---|
| 50 | visible: size/y - (edge/size/y * 2) - para/origin/y - para/indent/y
|
|---|
| 51 | pane/data: - para/scroll/y / (total - visible)
|
|---|
| 52 |
|
|---|
| 53 | ] face
|
|---|
| 54 |
|
|---|
| 55 | face/key-scroll?: false
|
|---|
| 56 | ]
|
|---|
| 57 | ]
|
|---|
| 58 | ]
|
|---|
| 59 | ]
|
|---|
| 60 | esc: none
|
|---|
| 61 | caret: none
|
|---|
| 62 | undo: copy []
|
|---|
| 63 | text-y: none
|
|---|
| 64 | key-scroll?: false ; this is set to true by edit/edit-text to bypass slider action
|
|---|
| 65 | action: make default-action [
|
|---|
| 66 | on-scroll: make function! [face scroll /page /local total visible] [
|
|---|
| 67 | total: second size-text face
|
|---|
| 68 | visible: face/size/y - (face/edge/size/y * 2) - face/para/origin/y - face/para/indent/y
|
|---|
| 69 | face/para/scroll/y: either page [
|
|---|
| 70 | min max face/para/scroll/y - (visible * sign? scroll/y) (visible - total) 0
|
|---|
| 71 | ][
|
|---|
| 72 | min max face/para/scroll/y - (scroll/y * sizes/font-height) (visible - total) 0
|
|---|
| 73 | ]
|
|---|
| 74 | ; Update slider dragger position to reflect para/scroll/y
|
|---|
| 75 | ; para/scroll is relative to edge/size + para/origin + (para/indent * 0x1)
|
|---|
| 76 | all [face/pane/data: - face/para/scroll/y / (total - visible)]
|
|---|
| 77 | show face
|
|---|
| 78 | ]
|
|---|
| 79 | ]
|
|---|
| 80 | rebind: make function! [] [
|
|---|
| 81 | color: colors/page
|
|---|
| 82 | para/margin/x: sizes/slider + 2
|
|---|
| 83 | ]
|
|---|
| 84 | init: make function! [/local p] [
|
|---|
| 85 | if find options 'info [
|
|---|
| 86 | feel: make feel [engage: none]
|
|---|
| 87 | all [color = colors/page color: colors/outline-light]
|
|---|
| 88 | ]
|
|---|
| 89 | para: make para [] ; avoid shared para object for scrollable input widget
|
|---|
| 90 | p: self
|
|---|
| 91 | text-y: second size-text self
|
|---|
| 92 | all [negative? size/x size/x: 10000 size/x: 4 + first size-text self]
|
|---|
| 93 | all [negative? size/y size/y: 10000 size/y: 8 + text-y]
|
|---|
| 94 | pane: make slider [
|
|---|
| 95 | tip: none
|
|---|
| 96 | offset: as-pair p/size/x - sizes/slider 0
|
|---|
| 97 | size: as-pair sizes/slider p/size/y
|
|---|
| 98 | span: case [
|
|---|
| 99 | none? p/span [none]
|
|---|
| 100 | all [find p/span #H find p/span #W] [#XH]
|
|---|
| 101 | find p/span #W [#X]
|
|---|
| 102 | find p/span #H [#H]
|
|---|
| 103 | true [none]
|
|---|
| 104 | ]
|
|---|
| 105 | options: [arrows]
|
|---|
| 106 | action: make default-action [
|
|---|
| 107 | on-click: make function! [face /local visible] [
|
|---|
| 108 | ; Only update slider/data if scroll was caused by a key (not by slider itself). Avoids recursion.
|
|---|
| 109 | unless parent-face/key-scroll? [
|
|---|
| 110 | visible: (parent-face/size/y - (parent-face/edge/size/y * 2) - parent-face/para/origin/y - parent-face/para/indent/y)
|
|---|
| 111 | parent-face/para/scroll/y: negate parent-face/text-y - visible * data
|
|---|
| 112 | if all [
|
|---|
| 113 | view*/caret
|
|---|
| 114 | parent-face = view*/focal-face
|
|---|
| 115 | ][
|
|---|
| 116 | ; Keep caret inside the visible part of the area
|
|---|
| 117 | ; view*/caret: offset-to-caret parent-face min max
|
|---|
| 118 | ; (caret-to-offset parent-face view*/caret) ; get the current position of the caret
|
|---|
| 119 | ; (sizes/font-height * 0x1) ; minimum, plus height of one line of text, to keep caret fully visible
|
|---|
| 120 | ; (parent-face/size - (face/size * 1x0) - (sizes/font-height * 0x1)) ; maximum, subtract height of one line of text
|
|---|
| 121 | ] ; -AntonR
|
|---|
| 122 | show parent-face
|
|---|
| 123 | ]
|
|---|
| 124 | parent-face/key-scroll?: false
|
|---|
| 125 | ]
|
|---|
| 126 | ]
|
|---|
| 127 | ratio: p/size/y - 4 / text-y
|
|---|
| 128 | ]
|
|---|
| 129 | pane/init
|
|---|
| 130 | ]
|
|---|
| 131 | ] |
|---|