textview.monkey2 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625
  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 after text has changed.
  7. #end
  8. Field TextChanged:Void()
  9. #rem monkeydoc Invoked after lines have been 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,eols2 )
  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 Cursor type for textviews.
  178. Supported cursor types are:
  179. | CursorType | Description
  180. |:--------------|:-----------
  181. | Block | Block cursor
  182. | Line | Single pixel vertical line
  183. | IBeam | IBeam cursor
  184. #end
  185. Enum CursorType
  186. Block
  187. Line
  188. IBeam
  189. End
  190. #rem monkeydoc The TextView class.
  191. #end
  192. Class TextView Extends ScrollableView
  193. #rem monkeydoc Invoked when cursor moves.
  194. #end
  195. Field CursorMoved:Void()
  196. #rem monkeydoc Creates a new text view.
  197. #end
  198. Method New()
  199. Style=GetStyle( "TextView" )
  200. ContentView.Style=GetStyle( "TextViewContent" )
  201. _lines.Push( New Line )
  202. UpdateColors()
  203. Document=New TextDocument
  204. End
  205. Method New( text:String )
  206. Self.New()
  207. Document.Text=text
  208. End
  209. Method New( doc:TextDocument )
  210. Self.New()
  211. Document=doc
  212. End
  213. #rem monkeydoc Text document.
  214. #end
  215. Property Document:TextDocument()
  216. Return _doc
  217. Setter( doc:TextDocument )
  218. If _doc _doc.LinesModified-=LinesModified
  219. _doc=doc
  220. _doc.LinesModified+=LinesModified
  221. _cursor=Clamp( _cursor,0,_doc.TextLength )
  222. _anchor=_cursor
  223. UpdateCursor()
  224. End
  225. Public
  226. #rem monkeydoc Text colors.
  227. #end
  228. Property TextColors:Color[]()
  229. Return _textColors
  230. Setter( textColors:Color[] )
  231. _textColors=textColors
  232. End
  233. #rem monkeydoc Cursor color.
  234. #end
  235. Property CursorColor:Color()
  236. Return _cursorColor
  237. Setter( cursorColor:Color )
  238. _cursorColor=cursorColor
  239. End
  240. #rem monkeydoc Selection color.
  241. #end
  242. Property SelectionColor:Color()
  243. Return _selColor
  244. Setter( selectionColor:Color )
  245. _selColor=selectionColor
  246. End
  247. #rem monkeydoc Cursor type.
  248. #end
  249. Property CursorType:CursorType()
  250. Return _cursorType
  251. Setter( type:CursorType )
  252. _cursorType=type
  253. End
  254. #rem monkeydoc @deprecated Use [[CursorType]].
  255. #end
  256. Property BlockCursor:Bool()
  257. Return _cursorType=CursorType.Block
  258. Setter( block:Bool )
  259. _cursorType=block ? CursorType.Block Else CursorType.Line
  260. End
  261. #rem monkeydoc Cursor blink rate.
  262. Set to 0 for non-blinking cursor.
  263. #end
  264. Property CursorBlinkRate:Float()
  265. Return _blinkRate
  266. Setter( blinkRate:Float )
  267. _blinkRate=blinkRate
  268. RequestRender()
  269. End
  270. #rem monkeydoc Text.
  271. #end
  272. Property Text:String()
  273. Return _doc.Text
  274. Setter( text:String )
  275. _doc.Text=text
  276. End
  277. #rem monkeydoc Read only flag.
  278. #end
  279. Property ReadOnly:Bool()
  280. Return _readOnly
  281. Setter( readOnly:Bool )
  282. _readOnly=readOnly
  283. End
  284. #rem monkeydoc Tabstop.
  285. #end
  286. Property TabStop:Int()
  287. Return _tabStop
  288. Setter( tabStop:Int )
  289. _tabStop=tabStop
  290. InvalidateStyle()
  291. End
  292. #rem monkeydoc WordWrap flag.
  293. #end
  294. Property WordWrap:Bool()
  295. Return _wordWrap
  296. Setter( wordWrap:Bool )
  297. _wordWrap=wordWrap
  298. InvalidateStyle()
  299. End
  300. #rem monkeydoc Cursor character index.
  301. #end
  302. Property Cursor:Int()
  303. Return _cursor
  304. End
  305. #rem monkeydoc Anchor character index.
  306. #end
  307. Property Anchor:Int()
  308. Return _anchor
  309. End
  310. #rem monkeydoc Line the cursor is on.
  311. #end
  312. Property CursorLine:Int()
  313. Return _doc.FindLine( _cursor )
  314. End
  315. #rem monkeydoc Cursor rect.
  316. #end
  317. Property CursorRect:Recti()
  318. Return _cursorRect
  319. End
  320. #rem monkeydoc Approximate character width.
  321. #end
  322. Property CharWidth:Int()
  323. Return _charw
  324. End
  325. #rem monkeydoc Approximate character height.
  326. #end
  327. Property CharHeight:Int()
  328. Return _charh
  329. End
  330. #rem monkeydoc Approximate line height.
  331. Deprecated! Use [[LineRect]] instead to properly deal with word wrap.
  332. #end
  333. Property LineHeight:Int()
  334. Return _charh
  335. End
  336. #rem monkeydoc True if undo available.
  337. #end
  338. Property CanUndo:Bool()
  339. Return Not _readOnly And Not _undos.Empty
  340. End
  341. #rem monkeydoc True if redo available.
  342. #end
  343. Property CanRedo:Bool()
  344. Return Not _readOnly And Not _redos.Empty
  345. End
  346. #rem monkeydoc True if cut available.
  347. #end
  348. Property CanCut:Bool()
  349. Return Not _readOnly And _anchor<>_cursor
  350. End
  351. #rem monkeydoc True if copy available.
  352. #end
  353. Property CanCopy:Bool()
  354. Return _anchor<>_cursor
  355. End
  356. #rem monkeydoc True if paste available.
  357. #end
  358. Property CanPaste:Bool()
  359. Return Not _readOnly And Not App.ClipboardTextEmpty
  360. End
  361. #rem monkeydoc Returns the rect containing a character at a given index.
  362. #end
  363. Method CharRect:Recti( index:Int )
  364. Local line:=_doc.FindLine( index )
  365. Local text:=_doc.Text
  366. Local i0:=_doc.StartOfLine( line )
  367. Local eol:=_doc.EndOfLine( line )
  368. Local x0:=0,y0:=_lines[line].rect.Top
  369. While i0<eol
  370. Local w:=WordWidth( text,i0,eol,x0 )
  371. If x0+w>_wrapw '-_charw
  372. y0+=_charh
  373. x0=0
  374. Endif
  375. Local l:=WordLength( text,i0,eol )
  376. If index<i0+l
  377. x0+=WordWidth( text,i0,index,x0 )
  378. Exit
  379. Endif
  380. x0+=w
  381. i0+=l
  382. Wend
  383. Local w:=_charw
  384. If i0<eol And text[i0]>32 w=_font.TextWidth( text.Slice( i0,i0+1 ) )
  385. Return New Recti( x0,y0,x0+w,y0+_charh )
  386. End
  387. #rem monkeydoc Returns the index of the character nearest to a given point.
  388. #end
  389. Method CharAtPoint:Int( p:Vec2i )
  390. Local line:=LineAtPoint( p )
  391. Local text:=_doc.Text
  392. Local i0:=_doc.StartOfLine( line )
  393. Local eol:=_doc.EndOfLine( line )
  394. Local x0:=0,y0:=_lines[line].rect.Top+_charh
  395. While i0<eol
  396. Local w:=WordWidth( text,i0,eol,x0 )
  397. If x0+w>_wrapw '-_charw
  398. If p.y<y0 Exit
  399. y0+=_charh
  400. x0=0
  401. Endif
  402. Local l:=WordLength( text,i0,eol )
  403. If p.x<x0+w And p.y<y0
  404. For Local i:=0 Until l
  405. x0+=WordWidth( text,i0,i0+1,x0 )
  406. If p.x<x0 Exit
  407. i0+=1
  408. Next
  409. Exit
  410. Endif
  411. x0+=w
  412. i0+=l
  413. Wend
  414. Return i0
  415. End
  416. #rem monkedoc Returns the index of the line nearest to a given point.
  417. #end
  418. Method LineAtPoint:Int( p:Vec2i )
  419. If p.y<=0 Return 0
  420. If p.y>=_lines.Top.rect.Top Return _lines.Length-1
  421. Local min:=0,max:=_lines.Length-1
  422. Repeat
  423. Local line:=(min+max)/2
  424. If p.y>=_lines[line].rect.Bottom
  425. min=line+1
  426. Else If max-min>1
  427. max=line
  428. Else
  429. Return min
  430. Endif
  431. Forever
  432. Return 0
  433. End
  434. #rem monkeydoc Gets the bounding rect for a line.
  435. #end
  436. Method LineRect:Recti( line:Int )
  437. If line>=0 And line<_lines.Length Return _lines[line].rect
  438. Return New Recti
  439. End
  440. #rem monkeydoc Clears all text.
  441. #end
  442. Method Clear()
  443. SelectAll()
  444. ReplaceText( "" )
  445. End
  446. #rem monkeydoc Move cursor to line.
  447. #end
  448. Method GotoLine( line:Int )
  449. _anchor=_doc.StartOfLine( line )
  450. _cursor=_anchor
  451. UpdateCursor()
  452. End
  453. #rem monkeydoc Selects a line.
  454. #end
  455. Method SelectLine( line:Int )
  456. SelectText( _doc.StartOfLine( line ),_doc.EndOfLine( line ) )
  457. End
  458. #rem monkeydoc Selects text in a range.
  459. #end
  460. Method SelectText( anchor:Int,cursor:Int )
  461. _anchor=Clamp( anchor,0,_doc.TextLength )
  462. _cursor=Clamp( cursor,0,_doc.TextLength )
  463. UpdateCursor()
  464. End
  465. #rem monkeydoc Appends text.
  466. #end
  467. Method AppendText( text:String )
  468. SelectText( _doc.TextLength,_doc.TextLength )
  469. ReplaceText( text )
  470. End
  471. #rem monkeydoc Replaces current selection.
  472. #end
  473. Method ReplaceText( text:String )
  474. Local undo:=New UndoOp
  475. undo.text=_doc.Text.Slice( Min( _anchor,_cursor ),Max( _anchor,_cursor ) )
  476. undo.anchor=Min( _anchor,_cursor )
  477. undo.cursor=undo.anchor+text.Length
  478. _undos.Push( undo )
  479. ReplaceText( _anchor,_cursor,text )
  480. End
  481. 'non-undoable
  482. #rem monkeydoc @hidden
  483. #end
  484. Method ReplaceText( anchor:Int,cursor:Int,text:String )
  485. _redos.Clear()
  486. _doc.ReplaceText( anchor,cursor,text )
  487. _cursor=Min( anchor,cursor )+text.Length
  488. _anchor=_cursor
  489. UpdateCursor()
  490. End
  491. #rem monkeydoc Performs an undo.
  492. #end
  493. Method Undo()
  494. If _readOnly Return
  495. If _undos.Empty Return
  496. Local undo:=_undos.Pop()
  497. Local text:=undo.text
  498. Local anchor:=undo.anchor
  499. Local cursor:=undo.cursor
  500. undo.text=_doc.Text.Slice( anchor,cursor )
  501. undo.cursor=anchor+text.Length
  502. _redos.Push( undo )
  503. _doc.ReplaceText( anchor,cursor,text )
  504. _cursor=anchor+text.Length
  505. _anchor=_cursor
  506. UpdateCursor()
  507. End
  508. #rem monkeydoc Performs a redo.
  509. #end
  510. Method Redo()
  511. If _readOnly Return
  512. If _redos.Empty Return
  513. Local undo:=_redos.Pop()
  514. Local text:=undo.text
  515. Local anchor:=undo.anchor
  516. Local cursor:=undo.cursor
  517. undo.text=_doc.Text.Slice( anchor,cursor )
  518. undo.cursor=anchor+text.Length
  519. _undos.Push( undo )
  520. _doc.ReplaceText( anchor,cursor,text )
  521. _cursor=anchor+text.Length
  522. _anchor=_cursor
  523. UpdateCursor()
  524. End
  525. #rem monkeydoc Selects all text.
  526. #end
  527. Method SelectAll()
  528. SelectText( 0,_doc.TextLength )
  529. End
  530. #rem monkeydoc Performs a cut.
  531. #end
  532. Method Cut()
  533. If _readOnly Return
  534. Copy()
  535. ReplaceText( "" )
  536. End
  537. #rem monkeydoc Performs a copy.
  538. #end
  539. Method Copy()
  540. Local min:=Min( _anchor,_cursor )
  541. Local max:=Max( _anchor,_cursor )
  542. Local text:=_doc.Text.Slice( min,max )
  543. App.ClipboardText=text
  544. End
  545. #rem monkeydoc Performs a paste.
  546. #end
  547. Method Paste()
  548. If _readOnly Return
  549. If App.ClipboardTextEmpty Return
  550. Local text:String=App.ClipboardText
  551. text=text.Replace( "~r~n","~n" )
  552. text=text.Replace( "~r","~n" )
  553. If text ReplaceText( text )
  554. End
  555. Protected
  556. Method OnThemeChanged() Override
  557. UpdateColors()
  558. End
  559. Method OnValidateStyle() Override
  560. Local style:=RenderStyle
  561. _font=style.Font
  562. _charw=_font.TextWidth( "X" )
  563. _charh=_font.Height
  564. _tabw=_charw*_tabStop
  565. UpdateLines()
  566. End
  567. Method OnMeasureContent:Vec2i() Override
  568. If _wordWrap Return New Vec2i( 0,0 )
  569. If _wrapw<>$7fffffff
  570. _wrapw=$7fffffff
  571. UpdateLines()
  572. Endif
  573. Return New Vec2i( _lines.Top.maxWidth+_charw,_lines.Top.rect.Bottom )
  574. End
  575. Method OnMeasureContent2:Vec2i( size:Vec2i ) Override
  576. If Not _wordWrap Return New Vec2i( 0,0 )
  577. If _wrapw<>size.x
  578. _wrapw=size.x
  579. UpdateLines()
  580. Endif
  581. Return New Vec2i( _lines.Top.maxWidth,_lines.Top.rect.Bottom )
  582. End
  583. Method OnRenderContent( canvas:Canvas ) Override
  584. If App.KeyView=Self And Not _blinkTimer RestartBlinkTimer()
  585. Local clip:=VisibleRect
  586. Local firstLine:=LineAtPoint( New Vec2i( 0,clip.Top ) )
  587. Local lastLine:=LineAtPoint( New Vec2i( 0,clip.Bottom-1 ) )+1
  588. If _cursor<>_anchor
  589. Local min:=CharRect( Min( _anchor,_cursor ) )
  590. Local max:=CharRect( Max( _anchor,_cursor ) )
  591. canvas.Color=_selColor
  592. If min.Y=max.Y
  593. canvas.DrawRect( min.Left,min.Top,max.Left-min.Left,min.Height )
  594. Else
  595. canvas.DrawRect( min.Left,min.Top,(clip.Right-min.Left),min.Height )
  596. canvas.DrawRect( 0,min.Bottom,clip.Right,max.Top-min.Bottom )
  597. canvas.DrawRect( 0,max.Top,max.Left,max.Height )
  598. Endif
  599. Endif
  600. If Not _readOnly And App.KeyView=Self And _blinkOn
  601. canvas.Color=_cursorColor
  602. Select _cursorType
  603. Case CursorType.Block
  604. canvas.DrawRect( _cursorRect )
  605. Case CursorType.IBeam
  606. canvas.DrawRect( _cursorRect.X,_cursorRect.Y,1,_cursorRect.Height )
  607. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y,5,1 )
  608. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y+_cursorRect.Height-1,5,1 )
  609. Default
  610. canvas.DrawRect( Max( _cursorRect.X-1,0 ),_cursorRect.Y,2,_cursorRect.Height )
  611. End
  612. Endif
  613. _textColors[0]=RenderStyle.TextColor
  614. For Local line:=firstLine Until lastLine
  615. RenderLine( canvas,line )
  616. Next
  617. End
  618. Method OnKeyDown:Bool( key:Key,modifiers:Modifier=Null )
  619. Select key
  620. Case Key.Backspace
  621. If _anchor=_cursor And _cursor>0 SelectText( _cursor-1,_cursor )
  622. ReplaceText( "" )
  623. Case Key.KeyDelete
  624. If _anchor=_cursor And _cursor<_doc.Text.Length SelectText( _cursor,_cursor+1 )
  625. ReplaceText( "" )
  626. Case Key.Tab
  627. Local min:=_doc.FindLine( Min( _cursor,_anchor ) )
  628. Local max:=_doc.FindLine( Max( _cursor,_anchor ) )
  629. If min=max
  630. ReplaceText( "~t" )
  631. Else
  632. 'block tab/untab
  633. Local lines:=New StringStack
  634. For Local i:=min Until max
  635. lines.Push( _doc.GetLine( i ) )
  636. Next
  637. Local go:=True
  638. If modifiers & Modifier.Shift
  639. For Local i:=0 Until lines.Length
  640. If Not lines[i].Trim()
  641. lines[i]+="~n"
  642. Continue
  643. Endif
  644. If lines[i][0]=9
  645. lines[i]=lines[i].Slice( 1 )+"~n"
  646. Continue
  647. Endif
  648. go=False
  649. Exit
  650. Next
  651. Else
  652. For Local i:=0 Until lines.Length
  653. lines[i]="~t"+lines[i]+"~n"
  654. Next
  655. Endif
  656. If go
  657. SelectText( _doc.StartOfLine( min ),_doc.StartOfLine( max ) )
  658. ReplaceText( lines.Join( "" ) )
  659. SelectText( _doc.StartOfLine( min ),_doc.StartOfLine( max ) )
  660. Return False
  661. Endif
  662. Endif
  663. Case Key.Enter,Key.KeypadEnter
  664. ReplaceText( "~n" )
  665. 'auto indent!
  666. Local line:=CursorLine
  667. If line>0
  668. Local ptext:=_doc.GetLine( line-1 )
  669. Local indent:=ptext
  670. For Local i:=0 Until ptext.Length
  671. If ptext[i]<=32 Continue
  672. indent=ptext.Slice( 0,i )
  673. Exit
  674. Next
  675. If indent ReplaceText( indent )
  676. Endif
  677. Case Key.Left
  678. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  679. _cursor=Min( _anchor,_cursor )
  680. Else If _cursor
  681. _cursor-=1
  682. Endif
  683. UpdateCursor()
  684. Case Key.Right
  685. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  686. _cursor=Max( _anchor,_cursor )
  687. Else If _cursor<_doc.Text.Length
  688. _cursor+=1
  689. Endif
  690. UpdateCursor()
  691. Case Key.Home
  692. _cursor=_doc.StartOfLine( CursorLine )
  693. UpdateCursor()
  694. Case Key.KeyEnd
  695. _cursor=_doc.EndOfLine( CursorLine )
  696. UpdateCursor()
  697. Case Key.Up
  698. MoveLine( -1 )
  699. Case Key.Down
  700. MoveLine( 1 )
  701. Case Key.PageUp
  702. Local n:=VisibleRect.Height/_charh-1 'shouldn't really use cliprect here...
  703. MoveLine( -n )
  704. Case Key.PageDown
  705. Local n:=VisibleRect.Height/_charh-1
  706. MoveLine( n )
  707. Default
  708. Return False
  709. End
  710. Return True
  711. End
  712. Method OnControlKeyDown:bool( key:Key,modifiers:Modifier=Null ) Virtual
  713. Select key
  714. Case Key.A
  715. SelectAll()
  716. Case Key.X
  717. Cut()
  718. Case Key.C
  719. Copy()
  720. Case Key.V
  721. Paste()
  722. Case Key.Z
  723. Undo()
  724. Case Key.Y
  725. Redo()
  726. Case Key.Home
  727. _cursor=0
  728. UpdateCursor()
  729. Return True
  730. Case Key.KeyEnd
  731. _cursor=_doc.TextLength
  732. UpdateCursor()
  733. Return True
  734. Case Key.Left
  735. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  736. _cursor=Min( _anchor,_cursor )
  737. Endif
  738. Local term:=New Int[1]
  739. Local start:=FindWord( _cursor,term )
  740. _cursor=Max( start-1,0 )
  741. Local text:=Text
  742. While _cursor And text[_cursor-1]<=32 And text[_cursor-1]<>10
  743. _cursor-=1
  744. Wend
  745. UpdateCursor()
  746. Return True
  747. Case Key.Right
  748. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  749. _cursor=Max( _anchor,_cursor )
  750. Endif
  751. Local term:=New Int[1]
  752. Local start:=FindWord( _cursor,term )
  753. _cursor=term[0]
  754. Local text:=Text
  755. While _cursor<text.Length And text[_cursor]<=32 And text[_cursor]<>10
  756. _cursor+=1
  757. Wend
  758. UpdateCursor()
  759. Return True
  760. End
  761. Return False
  762. End
  763. Method OnKeyEvent( event:KeyEvent ) Override
  764. If _readOnly
  765. If _macosMode
  766. If (event.Modifiers & Modifier.Gui) And (event.Key=Key.C Or event.Key=Key.A)
  767. 'Copy, Select All
  768. Else
  769. Return
  770. Endif
  771. Else
  772. If (event.Modifiers & Modifier.Control) And (event.Key=Key.C Or event.Key=Key.A)
  773. 'Copy, Select All
  774. Else
  775. Return
  776. Endif
  777. Endif
  778. Endif
  779. Select event.Type
  780. Case EventType.KeyDown,EventType.KeyRepeat
  781. If _macosMode
  782. If event.Modifiers & Modifier.Gui
  783. Select event.Key
  784. Case Key.Home,Key.KeyEnd
  785. Default
  786. If Not OnControlKeyDown( event.Key,event.Modifiers ) Return
  787. End
  788. Else If event.Modifiers & Modifier.Control
  789. Select event.Key
  790. Case Key.A
  791. OnKeyDown( Key.Home )
  792. Case Key.E
  793. OnKeyDown( Key.KeyEnd )
  794. End
  795. Else
  796. Select event.Key
  797. Case Key.Home
  798. OnControlKeyDown( Key.Home )
  799. Case Key.KeyEnd
  800. OnControlKeyDown( Key.KeyEnd )
  801. Default
  802. If Not OnKeyDown( event.Key,event.Modifiers ) Return
  803. End
  804. Endif
  805. Else
  806. If event.Modifiers & Modifier.Control
  807. If Not OnControlKeyDown( event.Key,event.Modifiers ) Return
  808. Else
  809. If Not OnKeyDown( event.Key,event.Modifiers ) Return
  810. Endif
  811. Endif
  812. If Not (event.Modifiers & Modifier.Shift) _anchor=_cursor
  813. Case EventType.KeyChar
  814. If _undos.Length
  815. Local undo:=_undos.Top
  816. If Not undo.text And _cursor=undo.cursor
  817. ReplaceText( _anchor,_cursor,event.Text )
  818. undo.cursor=_cursor
  819. Return
  820. Endif
  821. Endif
  822. ReplaceText( event.Text )
  823. End
  824. End
  825. Method OnContentMouseEvent( event:MouseEvent ) Override
  826. Select event.Type
  827. Case EventType.MouseDown
  828. Select event.Clicks
  829. Case 1
  830. _cursor=CharAtPoint( event.Location )
  831. If Not (event.Modifiers & Modifier.Shift) _anchor=_cursor
  832. _dragging=True
  833. MakeKeyView()
  834. UpdateCursor()
  835. Case 2
  836. Local term:=New Int[1]
  837. Local start:=FindWord( CharAtPoint( event.Location ),term )
  838. SelectText( start,term[0] )
  839. Case 3
  840. SelectLine( LineAtPoint( event.Location ) )
  841. End
  842. Return
  843. #rem
  844. Case EventType.MouseClick
  845. _cursor=CharAtPoint( event.Location )
  846. If Not (event.Modifiers & Modifier.Shift) _anchor=_cursor
  847. _dragging=True
  848. MakeKeyView()
  849. UpdateCursor()
  850. Case EventType.MouseDoubleClick
  851. Local term:=New Int[1]
  852. Local start:=FindWord( CharAtPoint( event.Location ),term )
  853. SelectText( start,term[0] )
  854. #end
  855. Case EventType.MouseUp
  856. _dragging=False
  857. Case EventType.MouseMove
  858. If _dragging
  859. _cursor=CharAtPoint( event.Location )
  860. UpdateCursor()
  861. Endif
  862. Case EventType.MouseWheel
  863. Return
  864. End
  865. event.Eat()
  866. End
  867. Private
  868. Struct Line
  869. Field rect:Recti
  870. Field maxWidth:Int
  871. End
  872. Class UndoOp
  873. Field text:String
  874. Field anchor:Int
  875. Field cursor:Int
  876. End
  877. Field _doc:TextDocument
  878. Field _lines:=New Stack<Line>
  879. Field _tabStop:Int=4
  880. Field _tabSpaces:String=" "
  881. Field _cursorColor:Color=New Color( 0,.5,1,1 )
  882. Field _selColor:Color=New Color( 1,1,1,.25 )
  883. Field _cursorType:CursorType
  884. Field _blinkRate:Float=0
  885. Field _blinkOn:Bool=True
  886. Field _blinkTimer:Timer
  887. #if __HOSTOS__="macos"
  888. Field _macosMode:Bool=True
  889. #else
  890. Field _macosMode:Bool=False
  891. #endif
  892. Field _textColors:Color[]
  893. Field _anchor:Int
  894. Field _cursor:Int
  895. Field _font:Font
  896. Field _charw:Int
  897. Field _charh:Int
  898. Field _tabw:Int
  899. Field _wordWrap:Bool=False
  900. Field _wrapw:Int=$7fffffff
  901. Field _cursorRect:Recti
  902. Field _vcursor:Vec2i
  903. Field _undos:=New Stack<UndoOp>
  904. Field _redos:=New Stack<UndoOp>
  905. Field _dragging:Bool
  906. Field _readOnly:Bool
  907. Method UpdateColors()
  908. CursorColor=App.Theme.GetColor( "textview-cursor" )
  909. SelectionColor=App.Theme.GetColor( "textview-selection" )
  910. Local colors:=New Color[8]
  911. For Local i:=0 Until 8
  912. colors[i]=App.Theme.GetColor( "textview-color"+i )
  913. Next
  914. TextColors=colors
  915. End
  916. Method CancelBlinkTimer()
  917. If Not _blinkTimer Return
  918. _blinkTimer.Cancel()
  919. _blinkTimer=Null
  920. _blinkOn=True
  921. End
  922. Method RestartBlinkTimer()
  923. CancelBlinkTimer()
  924. If Not _blinkRate Or App.KeyView<>Self Return
  925. _blinkTimer=New Timer( _blinkRate,Lambda()
  926. If App.KeyView<>Self Or Not _blinkRate
  927. CancelBlinkTimer()
  928. Return
  929. Endif
  930. _blinkOn=Not _blinkOn
  931. RequestRender()
  932. End )
  933. End
  934. Method UpdateCursor()
  935. Local rect:=CharRect( _cursor )
  936. EnsureVisible( rect )
  937. _vcursor=rect.Origin
  938. If rect<>_cursorRect
  939. RestartBlinkTimer()
  940. _cursorRect=rect
  941. CursorMoved()
  942. Endif
  943. RequestRender()
  944. End
  945. Method FindWord:Int( from:Int,term:Int[] )
  946. Local text:=Text
  947. If from<0
  948. term[0]=0
  949. Return 0
  950. Else If from>=text.Length
  951. term[0]=text.Length
  952. Return text.Length
  953. Else If text[from]=10
  954. term[0]=from+1
  955. Return from
  956. Endif
  957. Local start:=from,ends:=from+1
  958. If text[from]<=32
  959. While start And text[start-1]<=32 And text[start-1]<>10
  960. start-=1
  961. Wend
  962. While ends<text.Length And text[ends]<=32 And text[ends]<>10
  963. ends+=1
  964. Wend
  965. Else if IsIdent( text[start] )
  966. While start And IsIdent( text[start-1] )
  967. start-=1
  968. Wend
  969. While ends<text.Length And IsIdent( text[ends] )
  970. ends+=1
  971. Wend
  972. Else
  973. While start And text[start-1]>32 And Not IsIdent( text[start-1] )
  974. start-=1
  975. Wend
  976. While ends<text.Length And text[ends]>32 And Not IsIdent( text[ends] )
  977. ends+=1
  978. Wend
  979. Endif
  980. term[0]=ends
  981. Return start
  982. End
  983. Method WordLength:Int( text:String,i0:Int,eol:Int )
  984. Local i1:=i0
  985. If text[i1]<=32
  986. While i1<eol And text[i1]<=32
  987. i1+=1
  988. Wend
  989. Else If IsIdent( text[i1] )
  990. While i1<eol And IsIdent( text[i1] )
  991. i1+=1
  992. Wend
  993. Else
  994. While i1<eol And text[i1]>32 And Not IsIdent( text[i1] )
  995. i1+=1
  996. Wend
  997. Endif
  998. Return i1-i0
  999. End
  1000. Method WordWidth:Int( text:String,i0:Int,eol:Int,x0:Int )
  1001. Local i1:=i0,x1:=x0
  1002. If text[i0]<=32
  1003. While i1<eol And text[i1]<=32
  1004. If text[i1]=9
  1005. x1=Int( (x1+_tabw)/_tabw ) * _tabw
  1006. Else
  1007. x1+=_charw
  1008. Endif
  1009. i1+=1
  1010. Wend
  1011. Else
  1012. If IsIdent( text[i1] )
  1013. While i1<eol And IsIdent( text[i1] )
  1014. i1+=1
  1015. Wend
  1016. Else
  1017. While i1<eol And text[i1]>32 And Not IsIdent( text[i1] )
  1018. i1+=1
  1019. Wend
  1020. Endif
  1021. x1+=_font.TextWidth( text.Slice( i0,i1 ) )
  1022. Endif
  1023. Return x1-x0
  1024. End
  1025. Method MeasureLine:Vec2i( line:Int )
  1026. Local text:=_doc.Text
  1027. Local i0:=_doc.StartOfLine( line )
  1028. Local eol:=_doc.EndOfLine( line )
  1029. Local x0:=0,y0:=_charh,maxw:=0
  1030. While i0<eol
  1031. Local w:=WordWidth( text,i0,eol,x0 )
  1032. If x0+w>_wrapw '-_charw
  1033. maxw=Max( maxw,x0 )
  1034. y0+=_charh
  1035. x0=0
  1036. Endif
  1037. x0+=w
  1038. i0+=WordLength( text,i0,eol )
  1039. Wend
  1040. maxw=Max( maxw,x0 )
  1041. Return New Vec2i( maxw,y0 )
  1042. End
  1043. Method MoveLine( delta:Int )
  1044. Local vcursor:=_vcursor
  1045. _vcursor.y+=delta * _charh
  1046. _cursor=CharAtPoint( _vcursor )
  1047. UpdateCursor()
  1048. _vcursor.x=vcursor.x
  1049. End
  1050. Method UpdateLines()
  1051. Local liney:=0,maxWidth:=0
  1052. For Local i:=0 Until _lines.Length
  1053. Local size:=MeasureLine( i )
  1054. maxWidth=Max( maxWidth,size.x )
  1055. _lines.Data[i].maxWidth=maxWidth
  1056. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1057. liney+=size.y
  1058. Next
  1059. UpdateCursor()
  1060. End
  1061. Method RenderLine( canvas:Canvas,line:Int )
  1062. Local text:=_doc.Text
  1063. Local colors:=_doc.Colors
  1064. Local i0:=_doc.StartOfLine( line )
  1065. Local eol:=_doc.EndOfLine( line )
  1066. Local x0:=0,y0:=_lines[line].rect.Top
  1067. While i0<eol
  1068. Local w:=WordWidth( text,i0,eol,x0 )
  1069. Local l:=WordLength( text,i0,eol )
  1070. If x0+w>_wrapw '-_charw
  1071. y0+=_charh
  1072. x0=0
  1073. Endif
  1074. If text[i0]<=32
  1075. x0+=w
  1076. i0+=l
  1077. Continue
  1078. Endif
  1079. Local i1:=i0+l
  1080. While i0<i1
  1081. Local start:=i0
  1082. Local color:=colors[start]
  1083. i0+=1
  1084. While i0<i1 And colors[i0]=color
  1085. i0+=1
  1086. Wend
  1087. If color<0 Or color>=_textColors.Length color=0
  1088. canvas.Color=_textColors[color]
  1089. Local str:=text.Slice( start,i0 )
  1090. canvas.DrawText( str,x0,y0 )
  1091. x0+=_font.TextWidth( str )
  1092. Wend
  1093. Wend
  1094. End
  1095. Method LinesModified( first:Int,removed:Int,inserted:Int )
  1096. ' Print "Lines modified: first="+first+", removed="+removed+", inserted="+inserted+", _charh="+_charh
  1097. ValidateStyle()
  1098. Local last:=first+inserted+1
  1099. Local dlines:=inserted-removed
  1100. If dlines>=0
  1101. _lines.Resize( _lines.Length+dlines )
  1102. Local i:=_lines.Length
  1103. While i>last
  1104. i-=1
  1105. _lines[i]=_lines[i-dlines]
  1106. Wend
  1107. Endif
  1108. Local liney:=0,maxWidth:=0
  1109. If first
  1110. liney=_lines[first-1].rect.Bottom
  1111. maxWidth=_lines[first-1].maxWidth
  1112. Endif
  1113. For Local i:=first Until last
  1114. Local size:=MeasureLine( i )
  1115. maxWidth=Max( maxWidth,size.x )
  1116. _lines.Data[i].maxWidth=maxWidth
  1117. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1118. liney+=size.y
  1119. Next
  1120. If dlines<0
  1121. Local i:=last
  1122. While i<_lines.Length+dlines
  1123. _lines[i]=_lines[i-dlines]
  1124. i+=1
  1125. Wend
  1126. _lines.Resize( _lines.Length+dlines )
  1127. Endif
  1128. For Local i:=last Until _lines.Length
  1129. Local size:=_lines[i].rect.Size
  1130. maxWidth=Max( maxWidth,size.x )
  1131. _lines.Data[i].maxWidth=maxWidth
  1132. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1133. liney+=size.y
  1134. Next
  1135. If _cursor>_doc.TextLength
  1136. _cursor=_doc.TextLength
  1137. _anchor=_cursor
  1138. UpdateCursor()
  1139. RequestRender()
  1140. Else
  1141. _anchor=Min( _anchor,_doc.TextLength )
  1142. Endif
  1143. ' Print "Document width="+_lines.Top.maxWidth+", height="+_lines.Top.rect.Bottom
  1144. End
  1145. End