factor/basis/ui/gadgets/editors/editors.factor

566 lines
15 KiB
Factor
Raw Normal View History

! Copyright (C) 2006, 2008 Slava Pestov
2007-09-20 18:09:08 -04:00
! See http://factorcode.org/license.txt for BSD license.
USING: accessors arrays documents kernel math models
namespaces locals fry make opengl opengl.gl sequences strings
io.styles math.vectors sorting colors combinators assocs
math.order fry calendar alarms ui.clipboards ui.commands
ui.gadgets ui.gadgets.borders ui.gadgets.buttons
ui.gadgets.labels ui.gadgets.scrollers ui.gadgets.theme
ui.gadgets.menus ui.gadgets.wrappers ui.render ui.gestures
math.geometry.rect ;
2007-09-20 18:09:08 -04:00
IN: ui.gadgets.editors
TUPLE: editor < gadget
2007-09-20 18:09:08 -04:00
font color caret-color selection-color
caret mark
2008-11-20 23:13:32 -05:00
focused? blink blink-alarm ;
2007-09-20 18:09:08 -04:00
: <loc> ( -- loc ) { 0 0 } <model> ;
2007-09-20 18:09:08 -04:00
: init-editor-locs ( editor -- editor )
<loc> >>caret
<loc> >>mark ; inline
2007-09-20 18:09:08 -04:00
: editor-theme ( editor -- editor )
black >>color
red >>caret-color
selection-color >>selection-color
monospace-font >>font ; inline
2007-09-20 18:09:08 -04:00
: new-editor ( class -- editor )
new-gadget
<document> >>model
init-editor-locs
2008-07-11 01:46:15 -04:00
editor-theme ; inline
2007-10-31 01:04:54 -04:00
: <editor> ( -- editor )
editor new-editor ;
2007-09-20 18:09:08 -04:00
: activate-editor-model ( editor model -- )
2dup add-connection
dup activate-model
swap model>> add-loc ;
2007-09-20 18:09:08 -04:00
: deactivate-editor-model ( editor model -- )
2dup remove-connection
dup deactivate-model
swap model>> remove-loc ;
2007-09-20 18:09:08 -04:00
2008-11-20 23:14:35 -05:00
: blink-caret ( editor -- )
[ not ] change-blink relayout-1 ;
2008-11-21 00:29:16 -05:00
SYMBOL: blink-interval
750 milliseconds blink-interval set-global
2008-11-20 23:14:35 -05:00
: stop-blinking ( editor -- )
2008-11-20 23:15:07 -05:00
[ [ cancel-alarm ] when* f ] change-blink-alarm drop ;
2008-11-20 23:14:35 -05:00
2008-11-30 14:50:09 -05:00
: start-blinking ( editor -- )
[ stop-blinking ] [
t >>blink
dup '[ _ blink-caret ] blink-interval get every
>>blink-alarm drop
] bi ;
2008-11-20 23:14:35 -05:00
: restart-blinking ( editor -- )
dup focused?>> [
[ start-blinking ]
[ relayout-1 ]
2008-11-30 14:50:09 -05:00
bi
2008-11-20 23:14:35 -05:00
] [ drop ] if ;
2007-09-20 18:09:08 -04:00
M: editor graft*
2007-11-14 16:35:17 -05:00
dup
dup caret>> activate-editor-model
dup mark>> activate-editor-model ;
2007-09-20 18:09:08 -04:00
M: editor ungraft*
2007-11-14 16:35:17 -05:00
dup
2008-11-20 23:14:35 -05:00
dup stop-blinking
dup caret>> deactivate-editor-model
dup mark>> deactivate-editor-model ;
2007-09-20 18:09:08 -04:00
2008-08-31 17:19:24 -04:00
: editor-caret* ( editor -- loc ) caret>> value>> ;
2007-09-20 18:09:08 -04:00
2008-08-31 17:19:24 -04:00
: editor-mark* ( editor -- loc ) mark>> value>> ;
2007-09-20 18:09:08 -04:00
2007-12-30 21:15:59 -05:00
: set-caret ( loc editor -- )
[ model>> validate-loc ] keep
caret>> set-model ;
2007-12-30 21:15:59 -05:00
2007-09-20 18:09:08 -04:00
: change-caret ( editor quot -- )
2008-11-20 23:13:32 -05:00
[ [ [ editor-caret* ] [ model>> ] bi ] dip call ] [ drop ] 2bi
2007-12-30 21:15:59 -05:00
set-caret ; inline
2007-09-20 18:09:08 -04:00
: mark>caret ( editor -- )
2008-11-20 23:13:32 -05:00
[ editor-caret* ] [ mark>> ] bi set-model ;
2007-09-20 18:09:08 -04:00
: change-caret&mark ( editor quot -- )
2008-11-20 23:13:32 -05:00
[ change-caret ] [ drop mark>caret ] 2bi ; inline
2007-09-20 18:09:08 -04:00
: editor-line ( n editor -- str ) control-value nth ;
: editor-font* ( editor -- font ) font>> open-font ;
2007-09-20 18:09:08 -04:00
: line-height ( editor -- n )
editor-font* "" string-height ;
: y>line ( y editor -- line# )
line-height / >fixnum ;
:: point>loc ( point editor -- loc )
point second editor y>line {
{ [ dup 0 < ] [ drop { 0 0 } ] }
{ [ dup editor model>> last-line# > ] [ drop editor model>> doc-end ] }
[| n |
n
point first
editor editor-font*
n editor editor-line
x>offset 2array
]
} cond ;
2007-09-20 18:09:08 -04:00
: clicked-loc ( editor -- loc )
[ hand-rel ] keep point>loc ;
2007-09-20 18:09:08 -04:00
: click-loc ( editor model -- )
2008-11-20 23:13:32 -05:00
[ clicked-loc ] dip set-model ;
2007-09-20 18:09:08 -04:00
2008-11-20 23:13:32 -05:00
: focus-editor ( editor -- )
dup start-blinking
t >>focused?
relayout-1 ;
: unfocus-editor ( editor -- )
dup stop-blinking
f >>focused?
relayout-1 ;
2007-09-20 18:09:08 -04:00
: offset>x ( col# line# editor -- x )
[ editor-line ] keep editor-font* spin head-slice string-width ;
2007-09-20 18:09:08 -04:00
2008-11-20 23:13:32 -05:00
: loc>x ( loc editor -- x ) [ first2 swap ] dip offset>x ;
2007-09-20 18:09:08 -04:00
: line>y ( lines# editor -- y )
line-height * ;
: caret-loc ( editor -- loc )
[ editor-caret* ] keep
[ loc>x ] [ [ first ] dip line>y ] 2bi 2array ;
2007-09-20 18:09:08 -04:00
: caret-dim ( editor -- dim )
line-height 0 swap 2array ;
: scroll>caret ( editor -- )
2008-08-29 19:44:19 -04:00
dup graft-state>> second [
[
[ caret-loc ] [ caret-dim { 1 0 } v+ ] bi <rect>
] keep scroll>rect
] [ drop ] if ;
2007-09-20 18:09:08 -04:00
: draw-caret ( -- )
2008-11-20 23:13:32 -05:00
editor get [ focused?>> ] [ blink>> ] bi and [
2007-09-20 18:09:08 -04:00
editor get
2008-11-11 03:31:56 -05:00
[ caret-color>> gl-color ]
[
dup caret-loc origin get v+
swap caret-dim over v+
gl-line
] bi
2007-09-20 18:09:08 -04:00
] when ;
: line-translation ( n -- loc )
editor get line-height * 0.0 swap 2array ;
: translate-lines ( n -- )
line-translation gl-translate ;
: draw-line ( editor str -- )
2008-11-20 23:13:32 -05:00
[ font>> ] dip { 0 0 } draw-string ;
2007-09-20 18:09:08 -04:00
: first-visible-line ( editor -- n )
[
[ clip get rect-loc second origin get second - ] dip
y>line
] keep model>> validate-line ;
2007-09-20 18:09:08 -04:00
: last-visible-line ( editor -- n )
[
[ clip get rect-extent nip second origin get second - ] dip
y>line
] keep model>> validate-line 1+ ;
2007-09-20 18:09:08 -04:00
: with-editor ( editor quot -- )
[
swap
dup first-visible-line \ first-visible-line set
dup last-visible-line \ last-visible-line set
dup model>> document set
2007-09-20 18:09:08 -04:00
editor set
call
] with-scope ; inline
: visible-lines ( editor -- seq )
[ \ first-visible-line get \ last-visible-line get ] dip
control-value <slice> ;
2007-09-20 18:09:08 -04:00
: with-editor-translation ( n quot -- )
2008-11-20 23:13:32 -05:00
[ line-translation origin get v+ ] dip with-translation ;
2007-09-20 18:09:08 -04:00
inline
: draw-lines ( -- )
\ first-visible-line get [
editor get dup color>> gl-color
2007-09-20 18:09:08 -04:00
dup visible-lines
2008-01-09 17:36:30 -05:00
[ draw-line 1 translate-lines ] with each
2007-09-20 18:09:08 -04:00
] with-editor-translation ;
: selection-start/end ( editor -- start end )
[ editor-mark* ] [ editor-caret* ] bi sort-pair ;
2007-09-20 18:09:08 -04:00
: (draw-selection) ( x1 x2 -- )
2008-11-11 03:31:56 -05:00
over -
dup 0 = [ 2 + ] when
[ 0.0 2array ] [ editor get line-height 2array ] bi*
swap [ gl-fill-rect ] with-translation ;
2007-09-20 18:09:08 -04:00
: draw-selected-line ( start end n -- )
[ start/end-on-line ] keep
tuck [ editor get offset>x ] 2bi@
2007-09-20 18:09:08 -04:00
(draw-selection) ;
: draw-selection ( -- )
editor get selection-color>> gl-color
2007-09-20 18:09:08 -04:00
editor get selection-start/end
over first [
2008-12-02 21:46:21 -05:00
2dup '[
[ _ _ ] dip
draw-selected-line
2007-09-20 18:09:08 -04:00
1 translate-lines
2008-12-02 21:46:21 -05:00
] each-line
2007-09-20 18:09:08 -04:00
] with-editor-translation ;
M: editor draw-gadget*
[ draw-selection draw-lines draw-caret ] with-editor ;
M: editor pref-dim*
dup editor-font* swap control-value text-dim ;
2008-06-08 16:32:55 -04:00
: contents-changed ( model editor -- )
2008-07-11 01:46:15 -04:00
swap
over caret>> [ over validate-loc ] (change-model)
over mark>> [ over validate-loc ] (change-model)
drop relayout ;
2008-06-08 16:32:55 -04:00
: caret/mark-changed ( model editor -- )
2008-11-20 23:13:32 -05:00
nip [ restart-blinking ] [ scroll>caret ] bi ;
M: editor model-changed
{
2008-07-11 01:46:15 -04:00
{ [ 2dup model>> eq? ] [ contents-changed ] }
{ [ 2dup caret>> eq? ] [ caret/mark-changed ] }
{ [ 2dup mark>> eq? ] [ caret/mark-changed ] }
} cond ;
2007-09-20 18:09:08 -04:00
M: editor gadget-selection?
selection-start/end = not ;
M: editor gadget-selection
[ selection-start/end ] keep model>> doc-range ;
2007-09-20 18:09:08 -04:00
: remove-selection ( editor -- )
[ selection-start/end ] keep model>> remove-doc-range ;
2007-09-20 18:09:08 -04:00
M: editor user-input*
[ selection-start/end ] keep model>> set-doc-range t ;
2007-09-20 18:09:08 -04:00
: editor-string ( editor -- string )
model>> doc-string ;
2007-09-20 18:09:08 -04:00
: set-editor-string ( string editor -- )
model>> set-doc-string ;
2007-09-20 18:09:08 -04:00
M: editor gadget-text* editor-string % ;
: extend-selection ( editor -- )
2008-11-20 23:13:32 -05:00
dup request-focus
dup restart-blinking
dup caret>> click-loc ;
2008-02-06 20:23:39 -05:00
: mouse-elt ( -- element )
hand-click# get {
2008-02-06 20:23:39 -05:00
{ 1 T{ one-char-elt } }
{ 2 T{ one-word-elt } }
2008-02-06 20:23:39 -05:00
} at T{ one-line-elt } or ;
: drag-direction? ( loc editor -- ? )
2008-02-26 18:33:48 -05:00
editor-mark* before? ;
: drag-selection-caret ( loc editor element -- loc )
2008-11-20 23:13:32 -05:00
[
[ drag-direction? ] 2keep model>>
] dip prev/next-elt ? ;
: drag-selection-mark ( loc editor element -- loc )
2008-11-20 23:13:32 -05:00
[
[ drag-direction? not ] keep
[ editor-mark* ] [ model>> ] bi
] dip prev/next-elt ? ;
: drag-caret&mark ( editor -- caret mark )
dup clicked-loc swap mouse-elt
[ drag-selection-caret ] 3keep
drag-selection-mark ;
: drag-selection ( editor -- )
dup drag-caret&mark
pick mark>> set-model
swap caret>> set-model ;
2007-09-20 18:09:08 -04:00
: editor-cut ( editor clipboard -- )
dupd gadget-copy remove-selection ;
: delete/backspace ( editor quot -- )
2007-09-20 18:09:08 -04:00
over gadget-selection? [
drop remove-selection
2007-09-20 18:09:08 -04:00
] [
2008-11-20 23:13:32 -05:00
[ [ [ editor-caret* ] [ model>> ] bi ] dip call ]
[ drop model>> ]
2bi remove-doc-range
2007-09-20 18:09:08 -04:00
] if ; inline
: editor-delete ( editor elt -- )
'[ dupd _ next-elt ] delete/backspace ;
2007-09-20 18:09:08 -04:00
: editor-backspace ( editor elt -- )
'[ over [ _ prev-elt ] dip ] delete/backspace ;
2007-09-20 18:09:08 -04:00
: editor-select-prev ( editor elt -- )
'[ _ prev-elt ] change-caret ;
2007-09-20 18:09:08 -04:00
: editor-prev ( editor elt -- )
dupd editor-select-prev mark>caret ;
: editor-select-next ( editor elt -- )
'[ _ next-elt ] change-caret ;
2007-09-20 18:09:08 -04:00
: editor-next ( editor elt -- )
dupd editor-select-next mark>caret ;
: editor-select ( from to editor -- )
tuck caret>> set-model mark>> set-model ;
2007-09-20 18:09:08 -04:00
: select-elt ( editor elt -- )
2008-11-20 23:13:32 -05:00
[ [ [ editor-caret* ] [ model>> ] bi ] dip prev/next-elt ] [ drop ] 2bi
editor-select ;
2007-09-20 18:09:08 -04:00
: start-of-document ( editor -- ) T{ doc-elt } editor-prev ;
: end-of-document ( editor -- ) T{ doc-elt } editor-next ;
: position-caret ( editor -- )
mouse-elt dup T{ one-char-elt } =
[ drop dup extend-selection dup mark>> click-loc ]
[ select-elt ] if ;
2007-09-20 18:09:08 -04:00
2008-11-22 00:25:19 -05:00
: insert-newline ( editor -- ) "\n" swap user-input* drop ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-next-character ( editor -- )
T{ char-elt } editor-delete ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-previous-character ( editor -- )
T{ char-elt } editor-backspace ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-previous-word ( editor -- )
T{ word-elt } editor-delete ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-next-word ( editor -- )
T{ word-elt } editor-backspace ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-to-start-of-line ( editor -- )
T{ one-line-elt } editor-delete ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: delete-to-end-of-line ( editor -- )
T{ one-line-elt } editor-backspace ;
2007-09-20 18:09:08 -04:00
editor "general" f {
{ T{ key-down f f "DELETE" } delete-next-character }
{ T{ key-down f { S+ } "DELETE" } delete-next-character }
{ T{ key-down f f "BACKSPACE" } delete-previous-character }
{ T{ key-down f { S+ } "BACKSPACE" } delete-previous-character }
{ T{ key-down f { C+ } "DELETE" } delete-previous-word }
{ T{ key-down f { C+ } "BACKSPACE" } delete-next-word }
{ T{ key-down f { A+ } "DELETE" } delete-to-start-of-line }
{ T{ key-down f { A+ } "BACKSPACE" } delete-to-end-of-line }
} define-command-map
2008-06-08 16:32:55 -04:00
: paste ( editor -- ) clipboard get paste-clipboard ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: paste-selection ( editor -- ) selection get paste-clipboard ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: cut ( editor -- ) clipboard get editor-cut ;
2007-09-20 18:09:08 -04:00
editor "clipboard" f {
{ T{ paste-action } paste }
{ T{ button-up f f 2 } paste-selection }
{ T{ copy-action } com-copy }
{ T{ button-up } com-copy-selection }
{ T{ cut-action } cut }
} define-command-map
: previous-character ( editor -- )
dup gadget-selection? [
dup selection-start/end drop
over set-caret mark>caret
] [
T{ char-elt } editor-prev
] if ;
2007-09-20 18:09:08 -04:00
: next-character ( editor -- )
dup gadget-selection? [
dup selection-start/end nip
over set-caret mark>caret
] [
T{ char-elt } editor-next
] if ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: previous-line ( editor -- ) T{ line-elt } editor-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: next-line ( editor -- ) T{ line-elt } editor-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: previous-word ( editor -- ) T{ word-elt } editor-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: next-word ( editor -- ) T{ word-elt } editor-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: start-of-line ( editor -- ) T{ one-line-elt } editor-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: end-of-line ( editor -- ) T{ one-line-elt } editor-next ;
2007-09-20 18:09:08 -04:00
editor "caret-motion" f {
{ T{ button-down } position-caret }
{ T{ key-down f f "LEFT" } previous-character }
{ T{ key-down f f "RIGHT" } next-character }
{ T{ key-down f f "UP" } previous-line }
{ T{ key-down f f "DOWN" } next-line }
{ T{ key-down f { C+ } "LEFT" } previous-word }
{ T{ key-down f { C+ } "RIGHT" } next-word }
{ T{ key-down f f "HOME" } start-of-line }
{ T{ key-down f f "END" } end-of-line }
{ T{ key-down f { C+ } "HOME" } start-of-document }
{ T{ key-down f { C+ } "END" } end-of-document }
} define-command-map
2008-06-08 16:32:55 -04:00
: select-all ( editor -- ) T{ doc-elt } select-elt ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-line ( editor -- ) T{ one-line-elt } select-elt ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-word ( editor -- ) T{ one-word-elt } select-elt ;
2007-09-20 18:09:08 -04:00
2007-12-13 16:34:36 -05:00
: selected-word ( editor -- string )
dup gadget-selection?
[ dup select-word ] unless
gadget-selection ;
2008-06-08 16:32:55 -04:00
: select-previous-character ( editor -- )
T{ char-elt } editor-select-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-next-character ( editor -- )
T{ char-elt } editor-select-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-previous-line ( editor -- )
T{ line-elt } editor-select-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-next-line ( editor -- )
T{ line-elt } editor-select-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-previous-word ( editor -- )
T{ word-elt } editor-select-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-next-word ( editor -- )
T{ word-elt } editor-select-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-start-of-line ( editor -- )
T{ one-line-elt } editor-select-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-end-of-line ( editor -- )
T{ one-line-elt } editor-select-next ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-start-of-document ( editor -- )
T{ doc-elt } editor-select-prev ;
2007-09-20 18:09:08 -04:00
2008-06-08 16:32:55 -04:00
: select-end-of-document ( editor -- )
T{ doc-elt } editor-select-next ;
2007-09-20 18:09:08 -04:00
editor "selection" f {
{ T{ button-down f { S+ } 1 } extend-selection }
{ T{ drag } drag-selection }
2007-09-20 18:09:08 -04:00
{ T{ gain-focus } focus-editor }
{ T{ lose-focus } unfocus-editor }
{ T{ delete-action } remove-selection }
{ T{ select-all-action } select-all }
{ T{ key-down f { C+ } "l" } select-line }
{ T{ key-down f { S+ } "LEFT" } select-previous-character }
{ T{ key-down f { S+ } "RIGHT" } select-next-character }
{ T{ key-down f { S+ } "UP" } select-previous-line }
{ T{ key-down f { S+ } "DOWN" } select-next-line }
2008-02-10 21:32:26 -05:00
{ T{ key-down f { S+ C+ } "LEFT" } select-previous-word }
{ T{ key-down f { S+ C+ } "RIGHT" } select-next-word }
2007-09-20 18:09:08 -04:00
{ T{ key-down f { S+ } "HOME" } select-start-of-line }
{ T{ key-down f { S+ } "END" } select-end-of-line }
{ T{ key-down f { S+ C+ } "HOME" } select-start-of-document }
{ T{ key-down f { S+ C+ } "END" } select-end-of-document }
} define-command-map
: editor-menu ( editor -- )
{ cut com-copy paste } show-commands-menu ;
editor "misc" f {
{ T{ button-down f f 3 } editor-menu }
} define-command-map
2007-12-14 01:16:47 -05:00
! Multi-line editors
TUPLE: multiline-editor < editor ;
2007-12-14 01:16:47 -05:00
: <multiline-editor> ( -- editor )
multiline-editor new-editor ;
2007-12-14 01:16:47 -05:00
multiline-editor "general" f {
{ T{ key-down f f "RET" } insert-newline }
{ T{ key-down f { S+ } "RET" } insert-newline }
{ T{ key-down f f "ENTER" } insert-newline }
} define-command-map
2008-07-11 16:07:46 -04:00
TUPLE: source-editor < multiline-editor ;
2007-12-14 01:16:47 -05:00
: <source-editor> ( -- editor )
source-editor new-editor ;
! Fields wrap an editor and edit an external model
TUPLE: field < wrapper field-model editor ;
2007-12-14 01:16:47 -05:00
: field-theme ( gadget -- gadget )
gray <solid> >>boundary ; inline
2007-11-01 13:50:02 -04:00
: <field-border> ( gadget -- border )
2 <border>
{ 1 0 } >>fill
field-theme ;
2007-11-01 13:50:02 -04:00
: <field> ( model -- gadget )
<editor> dup <field-border> field new-wrapper
swap >>editor
swap >>field-model ;
2007-11-01 13:50:02 -04:00
M: field graft*
2008-08-31 17:19:24 -04:00
[ [ field-model>> value>> ] [ editor>> ] bi set-editor-string ]
[ dup editor>> model>> add-connection ]
bi ;
2007-11-01 13:50:02 -04:00
M: field ungraft*
dup editor>> model>> remove-connection ;
2007-11-01 13:50:02 -04:00
M: field model-changed
nip [ editor>> editor-string ] [ field-model>> ] bi set-model ;