textview.monkey2 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176
  1. Namespace mojox
  2. Alias TextHighlighter:Int( text:String,colors:Byte[],sol:Int,eol:Int,state:Int )
  3. #rem monkeydoc The TextDocument class.
  4. #end
  5. Class TextDocument
  6. #rem monkeydoc Invoked when text has changed.
  7. #end
  8. Field TextChanged:Void()
  9. #rem monkeydoc Invoked when lines modified.
  10. #end
  11. Field LinesModified:Void( first:Int,removed:Int,inserted:Int )
  12. #rem monkeydoc Creates a new text document.
  13. #end
  14. Method New()
  15. _lines.Push( New Line )
  16. End
  17. #rem monkeydoc Document text.
  18. #end
  19. Property Text:String()
  20. Return _text
  21. Setter( text:String )
  22. text=text.Replace( "~r~n","~n" )
  23. text=text.Replace( "~r","~n" )
  24. ReplaceText( 0,_text.Length,text )
  25. End
  26. #rem monkeydoc Length of doucment text.
  27. #end
  28. Property TextLength:Int()
  29. Return _text.Length
  30. End
  31. #rem monkeydoc Number of lines in document.
  32. #end
  33. Property NumLines:Int()
  34. Return _lines.Length
  35. End
  36. #rem monkeydoc @hidden
  37. #end
  38. Property Colors:Byte[]()
  39. Return _colors.Data
  40. End
  41. #rem monkeydoc @hidden
  42. #end
  43. Property TextHighlighter:TextHighlighter()
  44. Return _highlighter
  45. Setter( textHighlighter:TextHighlighter )
  46. _highlighter=textHighlighter
  47. End
  48. #rem monkeydoc @hidden
  49. #end
  50. Method LineState:Int( line:Int )
  51. If line>=0 And line<_lines.Length Return _lines[line].state
  52. Return -1
  53. End
  54. #rem monkeydoc Gets the index of the first character on a line.
  55. #end
  56. Method StartOfLine:Int( line:Int )
  57. If line<=0 Return 0
  58. If line<_lines.Length Return _lines[line-1].eol+1
  59. Return _text.Length
  60. End
  61. #rem monkeydoc Gets the index of the last character on a line.
  62. #end
  63. Method EndOfLine:Int( line:Int )
  64. If line<0 Return 0
  65. If line<_lines.Length Return _lines[line].eol
  66. Return _text.Length
  67. End
  68. #rem monkeydoc Finds the line containing a character.
  69. #end
  70. Method FindLine:Int( index:Int )
  71. If index<=0 Return 0
  72. If index>=_text.Length Return _lines.Length-1
  73. Local min:=0,max:=_lines.Length-1
  74. Repeat
  75. Local line:=(min+max)/2
  76. If index>_lines[line].eol
  77. min=line+1
  78. Else If max-min<2
  79. Return min
  80. Else
  81. max=line
  82. Endif
  83. Forever
  84. Return 0
  85. End
  86. #rem monkeydoc Gets line text.
  87. #end
  88. Method GetLine:String( line:Int )
  89. Return _text.Slice( StartOfLine( line ),EndOfLine( line ) )
  90. End
  91. #rem monkeydoc Appends text to the end of the document.
  92. #end
  93. Method AppendText( text:String )
  94. ReplaceText( _text.Length,_text.Length,text )
  95. End
  96. #rem monkeydoc Replaces text in the document.
  97. #end
  98. Method ReplaceText( anchor:Int,cursor:Int,text:String )
  99. Local min:=Min( anchor,cursor )
  100. Local max:=Max( anchor,cursor )
  101. Local eols1:=0,eols2:=0
  102. For Local i:=min Until max
  103. If _text[i]=10 eols1+=1
  104. Next
  105. For Local i:=0 Until text.Length
  106. If text[i]=10 eols2+=1
  107. Next
  108. Local dlines:=eols2-eols1
  109. Local dchars:=text.Length-(max-min)
  110. Local line0:=FindLine( min )
  111. Local line:=line0
  112. Local eol:=StartOfLine( line )-1
  113. ' Print "eols1="+eols1+", eols2="+eols2+", dlines="+dlines+", dchars="+dchars+" text="+text.Length
  114. 'Move data!
  115. '
  116. Local oldlen:=_text.Length
  117. _text=_text.Slice( 0,min )+text+_text.Slice( max )
  118. _colors.Resize( _text.Length )
  119. Local p:=_colors.Data.Data
  120. libc.memmove( p + min + text.Length, p + max , oldlen-max )
  121. libc.memset( p + min , 0 , text.Length )
  122. 'Update lines
  123. '
  124. If dlines>=0
  125. _lines.Resize( _lines.Length+dlines )
  126. Local i:=_lines.Length
  127. While i>line+eols2+1
  128. i-=1
  129. _lines.Data[i].eol=_lines[i-dlines].eol+dchars
  130. _lines.Data[i].state=_lines[i-dlines].state
  131. Wend
  132. Endif
  133. For Local i:=0 Until eols2+1
  134. eol=_text.Find( "~n",eol+1 )
  135. If eol=-1 eol=_text.Length
  136. _lines.Data[line+i].eol=eol
  137. _lines.Data[line+i].state=-1
  138. Next
  139. If dlines<0
  140. Local i:=line+eols2+1
  141. While i<_lines.Length+dlines
  142. _lines.Data[i].eol=_lines[i-dlines].eol+dchars
  143. _lines.Data[i].state=_lines[i-dlines].state
  144. i+=1
  145. Wend
  146. _lines.Resize( _lines.Length+dlines )
  147. Endif
  148. If _highlighter<>Null
  149. 'update highlighting
  150. '
  151. Local state:=-1
  152. If line state=_lines[line-1].state
  153. For Local i:=0 Until eols2+1
  154. state=_highlighter( _text,_colors.Data,StartOfLine( line ),EndOfLine( line ),state )
  155. _lines.Data[line].state=state
  156. line+=1
  157. Next
  158. While line<_lines.Length 'And state<>_lines[line].state
  159. state=_highlighter( _text,_colors.Data,StartOfLine( line ),EndOfLine( line ),state )
  160. _lines.Data[line].state=state
  161. line+=1
  162. End
  163. Endif
  164. LinesModified( line0,eols1+1,eols2+1 )
  165. TextChanged()
  166. End
  167. Private
  168. Struct Line
  169. Field eol:Int
  170. Field state:Int
  171. End
  172. Field _text:String
  173. Field _lines:=New Stack<Line>
  174. Field _colors:=New Stack<Byte>
  175. Field _highlighter:TextHighlighter
  176. End
  177. #rem monkeydoc The TextView class.
  178. #end
  179. Class TextView Extends ScrollableView
  180. #rem monkeydoc Invoked when cursor moves.
  181. #end
  182. Field CursorMoved:Void()
  183. #rem monkeydoc Creates a new text view.
  184. #end
  185. Method New()
  186. Style=GetStyle( "TextView" )
  187. _doc=New TextDocument
  188. _textColors=New Color[8]
  189. For Local i:=0 Until 7
  190. _textColors[i]=App.Theme.GetColor( "textview-color"+i )
  191. Next
  192. End
  193. Method New( text:String )
  194. Self.New()
  195. Document.Text=text
  196. End
  197. Method New( doc:TextDocument )
  198. Self.New()
  199. _doc=doc
  200. End
  201. #rem monkeydoc Text document.
  202. #end
  203. Property Document:TextDocument()
  204. Return _doc
  205. Setter( doc:TextDocument )
  206. _doc=doc
  207. _cursor=Clamp( _cursor,0,_doc.TextLength )
  208. _anchor=_cursor
  209. UpdateCursor()
  210. End
  211. #rem monkeydoc Text colors.
  212. #end
  213. Property TextColors:Color[]()
  214. Return _textColors
  215. Setter( textColors:Color[] )
  216. _textColors=textColors
  217. End
  218. #rem monkeydoc Selection color.
  219. #end
  220. Property SelectionColor:Color()
  221. Return _selColor
  222. Setter( selectionColor:Color )
  223. _selColor=selectionColor
  224. End
  225. #rem monkeydoc Cursor color.
  226. #end
  227. Property CursorColor:Color()
  228. Return _cursorColor
  229. Setter( cursorColor:Color )
  230. _cursorColor=cursorColor
  231. End
  232. #rem monkeydoc Block cursor flag.
  233. #end
  234. Property BlockCursor:Bool()
  235. Return _blockCursor
  236. Setter( blockCursor:Bool )
  237. _blockCursor=blockCursor
  238. End
  239. #rem monkeydoc Text.
  240. #end
  241. Property Text:String()
  242. Return _doc.Text
  243. Setter( text:String )
  244. _doc.Text=text
  245. End
  246. #rem monkeydoc Read only flags.
  247. #end
  248. Property ReadOnly:Bool()
  249. Return _readOnly
  250. Setter( readOnly:Bool )
  251. _readOnly=readOnly
  252. End
  253. #rem monkeydoc Tabstop.
  254. #end
  255. Property TabStop:Int()
  256. Return _tabStop
  257. Setter( tabStop:Int )
  258. _tabStop=tabStop
  259. InvalidateStyle()
  260. End
  261. #rem monkeydoc Cursor character index.
  262. #end
  263. Property Cursor:Int()
  264. Return _cursor
  265. End
  266. #rem monkeydoc Anchor character index.
  267. #end
  268. Property Anchor:Int()
  269. Return _anchor
  270. End
  271. #rem monkeydoc Cursor column.
  272. #end
  273. Property CursorColumn:Int()
  274. Return Column( _cursor )
  275. End
  276. #rem monkeydoc Cursor row.
  277. #end
  278. Property CursorRow:Int()
  279. Return Row( _cursor )
  280. End
  281. #rem monkeydoc Cursor rect.
  282. #end
  283. Property CursorRect:Recti()
  284. Return _cursorRect
  285. End
  286. #rem monkeydoc Approximate character width.
  287. #end
  288. Property CharWidth:Int()
  289. Return _charw
  290. End
  291. #rem monkeydoc Line height.
  292. #end
  293. Property LineHeight:Int()
  294. Return _charh
  295. End
  296. #rem monkeydoc True if undo available.
  297. #end
  298. Property CanUndo:Bool()
  299. Return Not _readOnly And Not _undos.Empty
  300. End
  301. #rem monkeydoc True if redo available.
  302. #end
  303. Property CanRedo:Bool()
  304. Return Not _readOnly And Not _redos.Empty
  305. End
  306. #rem monkeydoc True if cut available.
  307. #end
  308. Property CanCut:Bool()
  309. Return Not _readOnly And _anchor<>_cursor
  310. End
  311. #rem monkeydoc True if copy available.
  312. #end
  313. Property CanCopy:Bool()
  314. Return _anchor<>_cursor
  315. End
  316. #rem monkeydoc True if paste available.
  317. #end
  318. Property CanPaste:Bool()
  319. Return Not _readOnly And Not App.ClipboardTextEmpty
  320. End
  321. #rem monkeydoc Clears all text.
  322. #end
  323. Method Clear()
  324. SelectAll()
  325. ReplaceText( "" )
  326. End
  327. #rem monkeydoc Move cursor to line.
  328. #end
  329. Method GotoLine( line:Int )
  330. _anchor=_doc.StartOfLine( line )
  331. _cursor=_anchor
  332. UpdateCursor()
  333. End
  334. #rem monkeydoc Selects text in a range.
  335. #end
  336. Method SelectText( anchor:Int,cursor:Int )
  337. _anchor=Clamp( anchor,0,_doc.TextLength )
  338. _cursor=Clamp( cursor,0,_doc.TextLength )
  339. UpdateCursor()
  340. End
  341. #rem monkeydoc Appends text.
  342. #end
  343. Method AppendText( text:String )
  344. ' Local anchor:=_anchor
  345. ' Local cursor:=_cursor
  346. SelectText( _doc.TextLength,_doc.TextLength )
  347. ReplaceText( text )
  348. ' SelectText( anchor,cursor )
  349. End
  350. #rem monkeydoc Replaces current selection.
  351. #end
  352. Method ReplaceText( text:String )
  353. Local undo:=New UndoOp
  354. undo.text=_doc.Text.Slice( Min( _anchor,_cursor ),Max( _anchor,_cursor ) )
  355. undo.anchor=Min( _anchor,_cursor )
  356. undo.cursor=undo.anchor+text.Length
  357. _undos.Push( undo )
  358. ReplaceText( _anchor,_cursor,text )
  359. End
  360. 'non-undoable
  361. #rem monkeydoc @hidden
  362. #end
  363. Method ReplaceText( anchor:Int,cursor:Int,text:String )
  364. _redos.Clear()
  365. _doc.ReplaceText( anchor,cursor,text )
  366. _cursor=Min( anchor,cursor )+text.Length
  367. _anchor=_cursor
  368. UpdateCursor()
  369. End
  370. #rem monkeydoc Performs an undo.
  371. #end
  372. Method Undo()
  373. If _readOnly Return
  374. If _undos.Empty Return
  375. Local undo:=_undos.Pop()
  376. Local text:=undo.text
  377. Local anchor:=undo.anchor
  378. Local cursor:=undo.cursor
  379. undo.text=_doc.Text.Slice( anchor,cursor )
  380. undo.cursor=anchor+text.Length
  381. _redos.Push( undo )
  382. _doc.ReplaceText( anchor,cursor,text )
  383. _cursor=anchor+text.Length
  384. _anchor=_cursor
  385. UpdateCursor()
  386. End
  387. #rem monkeydoc Performs a redo.
  388. #end
  389. Method Redo()
  390. If _readOnly Return
  391. If _redos.Empty Return
  392. Local undo:=_redos.Pop()
  393. Local text:=undo.text
  394. Local anchor:=undo.anchor
  395. Local cursor:=undo.cursor
  396. undo.text=_doc.Text.Slice( anchor,cursor )
  397. undo.cursor=anchor+text.Length
  398. _undos.Push( undo )
  399. _doc.ReplaceText( anchor,cursor,text )
  400. _cursor=anchor+text.Length
  401. _anchor=_cursor
  402. UpdateCursor()
  403. End
  404. #rem monkeydoc Selects all text.
  405. #end
  406. Method SelectAll()
  407. SelectText( 0,_doc.TextLength )
  408. End
  409. #rem monkeydoc Performs a cut.
  410. #end
  411. Method Cut()
  412. If _readOnly Return
  413. Copy()
  414. ReplaceText( "" )
  415. End
  416. #rem monkeydoc Performs a copy.
  417. #end
  418. Method Copy()
  419. Local min:=Min( _anchor,_cursor )
  420. Local max:=Max( _anchor,_cursor )
  421. Local text:=_doc.Text.Slice( min,max )
  422. App.ClipboardText=text
  423. End
  424. #rem monkeydoc Performs a paste.
  425. #end
  426. Method Paste()
  427. If _readOnly Return
  428. If App.ClipboardTextEmpty Return
  429. Local text:String=App.ClipboardText
  430. text=text.Replace( "~r~n","~n" )
  431. text=text.Replace( "~r","~n" )
  432. If text ReplaceText( text )
  433. End
  434. Private
  435. Class UndoOp
  436. Field text:String
  437. Field anchor:Int
  438. Field cursor:Int
  439. End
  440. Field _doc:TextDocument
  441. Field _tabStop:Int=4
  442. Field _tabSpaces:String=" "
  443. Field _cursorColor:Color=New Color( 0,.5,1,1 )
  444. Field _selColor:Color=New Color( 1,1,1,.25 )
  445. Field _blockCursor:Bool=True
  446. #if __HOSTOS__="macos"
  447. Field _macosMode:Bool=True
  448. #else
  449. Field _macosMode:Bool=False
  450. #endif
  451. Field _textColors:Color[]
  452. Field _anchor:Int
  453. Field _cursor:Int
  454. Field _font:Font
  455. Field _charw:Int
  456. Field _charh:Int
  457. Field _tabw:Int
  458. Field _cursorRect:Recti
  459. Field _columnX:Int
  460. Field _undos:=New Stack<UndoOp>
  461. Field _redos:=New Stack<UndoOp>
  462. Field _dragging:Bool
  463. Field _readOnly:Bool
  464. Method Row:Int( index:Int )
  465. Return _doc.FindLine( index )
  466. End
  467. Method Column:Int( index:Int )
  468. Return index-_doc.StartOfLine( _doc.FindLine( index ) )
  469. End
  470. Method UpdateCursor()
  471. ValidateStyle()
  472. Local rect:=MakeCursorRect( _cursor )
  473. EnsureVisible( rect )
  474. If rect<>_cursorRect
  475. _cursorRect=rect
  476. _columnX=rect.X
  477. CursorMoved()
  478. Endif
  479. RequestRender()
  480. End
  481. Method MakeCursorRect:Recti( cursor:Int )
  482. ValidateStyle()
  483. Local line:=_doc.FindLine( cursor )
  484. Local text:=_doc.GetLine( line )
  485. Local x:=0.0,i0:=0,e:=cursor-_doc.StartOfLine( line )
  486. While i0<e
  487. Local i1:=text.Find( "~t",i0 )
  488. If i1=-1 i1=e
  489. If i1>i0
  490. If i1>e i1=e
  491. x+=_font.TextWidth( text.Slice( i0,i1 ) )
  492. If i1=e Exit
  493. Endif
  494. x=Int( (x+_tabw)/_tabw ) * _tabw
  495. i0=i1+1
  496. Wend
  497. Local w:=_charw
  498. If e<text.Length
  499. If text[e]=9
  500. ' w=Int( (x+_tabw)/_tabw ) * _tabw-x
  501. Else
  502. w=_font.TextWidth( text.Slice( e,e+1 ) )
  503. Endif
  504. Endif
  505. Local y:=line*_charh
  506. Return New Recti( x,y,x+w,y+_charh )
  507. End
  508. Method PointXToIndex:Int( px:Int,line:Int )
  509. ValidateStyle()
  510. Local text:=_doc.GetLine( line )
  511. Local sol:=_doc.StartOfLine( line )
  512. Local x:=0.0,i0:=0,e:=text.Length
  513. While i0<e
  514. Local i1:=text.Find( "~t",i0 )
  515. If i1=-1 i1=e
  516. If i1>i0
  517. For Local i:=i0 Until i1
  518. x+=_font.TextWidth( text.Slice( i,i+1 ) )
  519. If px<x Return sol+i
  520. Next
  521. If i1=e Exit
  522. Endif
  523. x=Int( (x+_tabw)/_tabw ) * _tabw
  524. If px<x Return sol+i0
  525. i0=i1+1
  526. Wend
  527. Return sol+e
  528. End
  529. Method PointToIndex:Int( p:Vec2i )
  530. If p.y<0 Return 0
  531. Local line:=p.y/_charh
  532. If line>_doc.NumLines Return _doc.TextLength
  533. Return PointXToIndex( p.x,line )
  534. End
  535. Method MoveLine( delta:Int )
  536. Local line:=Clamp( Row( _cursor )+delta,0,_doc.NumLines-1 )
  537. _cursor=PointXToIndex( _columnX,line )
  538. Local x:=_columnX
  539. UpdateCursor()
  540. _columnX=x
  541. End
  542. Protected
  543. Method OnValidateStyle() Override
  544. Local style:=RenderStyle
  545. _font=style.Font
  546. _charw=_font.TextWidth( "X" )
  547. _charh=_font.Height
  548. _tabw=_charw*_tabStop
  549. UpdateCursor()
  550. End
  551. Method OnMeasureContent:Vec2i() Override
  552. Return New Vec2i( 320*_charw,_doc.NumLines*_charh )
  553. ' Return New Vec2i( 160,_doc.NumLines*_charh )
  554. End
  555. Method OnRenderContent( canvas:Canvas ) Override
  556. Local clip:=VisibleRect
  557. Local firstVisLine:=Max( clip.Top/_charh,0 )
  558. Local lastVisLine:=Min( (clip.Bottom-1)/_charh+1,_doc.NumLines )
  559. If _cursor<>_anchor
  560. Local min:=MakeCursorRect( Min( _anchor,_cursor ) )
  561. Local max:=MakeCursorRect( Max( _anchor,_cursor ) )
  562. canvas.Color=_selColor
  563. If min.Y=max.Y
  564. canvas.DrawRect( min.Left,min.Top,max.Left-min.Left,min.Height )
  565. Else
  566. canvas.DrawRect( min.Left,min.Top,(clip.Right-min.Left),min.Height )
  567. canvas.DrawRect( 0,min.Bottom,clip.Right,max.Top-min.Bottom )
  568. canvas.DrawRect( 0,max.Top,max.Left,max.Height )
  569. Endif
  570. Else If Not _readOnly And App.KeyView=Self
  571. canvas.Color=_cursorColor
  572. If _blockCursor
  573. canvas.DrawRect( _cursorRect.X,_cursorRect.Y,_cursorRect.Width,_cursorRect.Height )
  574. Else
  575. canvas.DrawRect( _cursorRect.X-0,_cursorRect.Y,2,_cursorRect.Height )
  576. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y,6,2 )
  577. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y+_cursorRect.Height-2,6,2 )
  578. Endif
  579. Endif
  580. _textColors[0]=RenderStyle.TextColor
  581. For Local line:=firstVisLine Until lastVisLine
  582. Local sol:=_doc.StartOfLine( line )
  583. Local eol:=_doc.EndOfLine( line )
  584. Local text:=_doc.Text.Slice( sol,eol )
  585. Local colors:=_doc.Colors
  586. Local x:=0,y:=line*_charh,i0:=0
  587. While i0<text.Length
  588. Local i1:=text.Find( "~t",i0 )
  589. If i1=-1 i1=text.Length
  590. If i1>i0
  591. Local color:=colors[sol+i0]
  592. Local start:=i0
  593. Repeat
  594. While i0<i1 And colors[sol+i0]=color
  595. i0+=1
  596. Wend
  597. If i0>start
  598. If color<0 Or color>=_textColors.Length color=0
  599. canvas.Color=_textColors[color]
  600. Local t:=text.Slice( start,i0 )
  601. canvas.DrawText( t,x,y )
  602. x+=Style.Font.TextWidth( t )
  603. Endif
  604. If i0=i1 Exit
  605. color=colors[sol+i0]
  606. start=i0
  607. i0+=1
  608. Forever
  609. If i1=text.Length Exit
  610. Endif
  611. x=Int( (x+_tabw) / _tabw ) * _tabw
  612. i0=i1+1
  613. Wend
  614. Next
  615. End
  616. Method OnKeyDown:Bool( key:Key,modifiers:Modifier=Null )
  617. Select key
  618. Case Key.Backspace
  619. If _anchor=_cursor And _cursor>0 SelectText( _cursor-1,_cursor )
  620. ReplaceText( "" )
  621. Case Key.KeyDelete
  622. If _anchor=_cursor And _cursor<_doc.Text.Length SelectText( _cursor,_cursor+1 )
  623. ReplaceText( "" )
  624. Case Key.Tab
  625. Local min:=_doc.FindLine( Min( _cursor,_anchor ) )
  626. Local max:=_doc.FindLine( Max( _cursor,_anchor ) )
  627. If min=max
  628. ReplaceText( "~t" )
  629. Else
  630. 'block tab/untab
  631. Local lines:=New StringStack
  632. For Local i:=min Until max
  633. lines.Push( _doc.GetLine( i ) )
  634. Next
  635. Local go:=True
  636. If modifiers & Modifier.Shift
  637. For Local i:=0 Until lines.Length
  638. If Not lines[i].Trim()
  639. lines[i]+="~n"
  640. Continue
  641. Endif
  642. If lines[i][0]=9
  643. lines[i]=lines[i].Slice( 1 )+"~n"
  644. Continue
  645. Endif
  646. go=False
  647. Exit
  648. Next
  649. Else
  650. For Local i:=0 Until lines.Length
  651. lines[i]="~t"+lines[i]+"~n"
  652. Next
  653. Endif
  654. If go
  655. SelectText( _doc.StartOfLine( min ),_doc.StartOfLine( max ) )
  656. ReplaceText( lines.Join( "" ) )
  657. SelectText( _doc.StartOfLine( min ),_doc.StartOfLine( max ) )
  658. Return False
  659. Endif
  660. Endif
  661. Case Key.Enter
  662. ReplaceText( "~n" )
  663. 'auto indent!
  664. Local line:=CursorRow
  665. If line>0
  666. Local ptext:=_doc.GetLine( line-1 )
  667. Local indent:=ptext
  668. For Local i:=0 Until ptext.Length
  669. If ptext[i]<=32 Continue
  670. indent=ptext.Slice( 0,i )
  671. Exit
  672. Next
  673. If indent ReplaceText( indent )
  674. Endif
  675. Case Key.Left
  676. If _cursor
  677. _cursor-=1
  678. UpdateCursor()
  679. Endif
  680. Case Key.Right
  681. If _cursor<_doc.Text.Length
  682. _cursor+=1
  683. UpdateCursor()
  684. Endif
  685. Case Key.Home
  686. _cursor=_doc.StartOfLine( Row( _cursor ) )
  687. UpdateCursor()
  688. Case Key.KeyEnd
  689. _cursor=_doc.EndOfLine( Row( _cursor ) )
  690. UpdateCursor()
  691. Case Key.Up
  692. MoveLine( -1 )
  693. Case Key.Down
  694. MoveLine( 1 )
  695. Case Key.PageUp
  696. Local n:=VisibleRect.Height/_charh-1 'shouldn't really use cliprect here...
  697. MoveLine( -n )
  698. Case Key.PageDown
  699. Local n:=VisibleRect.Height/_charh-1
  700. MoveLine( n )
  701. Default
  702. Return False
  703. End
  704. Return True
  705. End
  706. Method OnControlKeyDown:bool( key:Key )
  707. Select key
  708. Case Key.A
  709. SelectAll()
  710. Case Key.X
  711. Cut()
  712. Case Key.C
  713. Copy()
  714. Case Key.V
  715. Paste()
  716. Case Key.Z
  717. Undo()
  718. Case Key.Y
  719. Redo()
  720. Case Key.Home
  721. _cursor=0
  722. UpdateCursor()
  723. Return True
  724. Case Key.KeyEnd
  725. _cursor=_doc.TextLength
  726. UpdateCursor()
  727. Return True
  728. End
  729. Return False
  730. End
  731. Method OnKeyEvent( event:KeyEvent ) Override
  732. If _readOnly Return
  733. Select event.Type
  734. Case EventType.KeyDown,EventType.KeyRepeat
  735. If _macosMode
  736. If event.Modifiers & Modifier.Gui
  737. Select event.Key
  738. Case Key.A,Key.X,Key.C,Key.V,Key.Z,Key.Y
  739. If Not OnControlKeyDown( event.Key ) Return
  740. End
  741. Else If event.Modifiers & Modifier.Control
  742. Select event.Key
  743. Case Key.A
  744. OnKeyDown( Key.Home )
  745. Case Key.E
  746. OnKeyDown( Key.KeyEnd )
  747. End
  748. Else
  749. Select event.Key
  750. Case Key.Home
  751. OnControlKeyDown( Key.Home )
  752. Case Key.KeyEnd
  753. OnControlKeyDown( Key.KeyEnd )
  754. Default
  755. If Not OnKeyDown( event.Key,event.Modifiers ) Return
  756. End
  757. Endif
  758. Else
  759. If event.Modifiers & Modifier.Control
  760. If Not OnControlKeyDown( event.Key ) Return
  761. Else
  762. If Not OnKeyDown( event.Key,event.Modifiers ) Return
  763. Endif
  764. Endif
  765. If Not (event.Modifiers & Modifier.Shift) _anchor=_cursor
  766. Case EventType.KeyChar
  767. If _undos.Length
  768. Local undo:=_undos.Top
  769. If Not undo.text And _cursor=undo.cursor
  770. ReplaceText( _anchor,_cursor,event.Text )
  771. undo.cursor=_cursor
  772. Return
  773. Endif
  774. Endif
  775. ReplaceText( event.Text )
  776. End
  777. End
  778. Method OnContentMouseEvent( event:MouseEvent ) Override
  779. Select event.Type
  780. Case EventType.MouseDown
  781. Return
  782. Case EventType.MouseClick
  783. _cursor=PointToIndex( event.Location )
  784. _anchor=_cursor
  785. _dragging=True
  786. MakeKeyView()
  787. UpdateCursor()
  788. Case EventType.MouseUp
  789. _dragging=False
  790. Case EventType.MouseMove
  791. If _dragging
  792. _cursor=PointToIndex( event.Location )
  793. UpdateCursor()
  794. Endif
  795. Case EventType.MouseWheel
  796. Return
  797. End
  798. event.Eat()
  799. End
  800. ' Method OnKeyViewChanged( oldKeyView:View,newKeyView:View ) Override
  801. ' If newKeyView=Self UpdateCursor()
  802. ' End
  803. End