@@ -9,9 +9,12 @@ package tigerjython.ui.editor
99
1010import javafx .application .Platform
1111import javafx .beans .value .{ChangeListener , ObservableValue }
12- import javafx .scene .Scene
13- import javafx .scene .control .{TextField , ToolBar }
12+ import javafx .scene .{ Group , Node , Scene }
13+ import javafx .scene .control .{Button , Label , TextField , ToolBar }
1414import javafx .scene .input .KeyCode
15+ import javafx .scene .shape .{Line , StrokeLineCap }
16+
17+ import java .lang
1518
1619/**
1720 * A simple bar that lets you enter something to be found in the document.
@@ -20,19 +23,44 @@ class FindBar(val editorTab: EditorTab) extends ToolBar {
2023
2124 private val editor = editorTab.editor
2225 val findTextField : TextField = new TextField ()
26+ private val countLabel : Label = new Label ()
27+ private val findNextButton = new Button ()
28+ private val findPrevButton = new Button ()
29+
30+ private var startPos : Int = 0
31+ private var endPos : Int = 0
2332
24- private var startIndex : Int = 0
25- private var endIndex : Int = 0
2633 private var currentIndex : Int = 0
2734 private var searchText : String = _
35+ private var fullText : String = _
36+ private var fullTextLC : String = _
37+
38+ private var occurrences : Array [Int ] = _
2839
2940 {
30- getItems.add (findTextField)
41+ getItems.addAll (findTextField, findNextButton, findPrevButton, countLabel )
3142 setPrefHeight(48 )
3243 setMinHeight(32 )
33- setPrefWidth(300 )
44+ findTextField.setPrefWidth(300 )
45+ findTextField.prefWidthProperty().bind(this .widthProperty().divide(3 ))
46+ findNextButton.setGraphic(createDownImage)
47+ findNextButton.getStyleClass.add(" download-text-btn" )
48+ findNextButton.setOnAction(_ => findNext())
49+ findNextButton.setFocusTraversable(false )
50+ findPrevButton.setGraphic(createUpImage)
51+ findPrevButton.getStyleClass.add(" download-text-btn" )
52+ findPrevButton.setOnAction(_ => findPrev())
53+ findPrevButton.setFocusTraversable(false )
3454 }
3555
56+ focusedProperty().addListener(new ChangeListener [java.lang.Boolean ] {
57+ override def changed (observableValue : ObservableValue [_ <: lang.Boolean ], oldValue : lang.Boolean , newValue : lang.Boolean ): Unit = {
58+ if (newValue && ! oldValue) {
59+ updateText()
60+ }
61+ }
62+ })
63+
3664 findTextField.sceneProperty().addListener(new ChangeListener [Scene ] {
3765 override def changed (observableValue : ObservableValue [_ <: Scene ], oldValue : Scene , newValue : Scene ): Unit = {
3866 if (oldValue != newValue && newValue != null )
@@ -57,50 +85,122 @@ class FindBar(val editorTab: EditorTab) extends ToolBar {
5785 }
5886 })
5987
88+ private def createDownImage : Node = {
89+ val result = new Group ()
90+ val lines = Array (
91+ new Line (- 4 , - 2 , 0 , 2 ),
92+ new Line (0 , 2 , 4 , - 2 ),
93+ )
94+ for (line <- lines)
95+ line.setStrokeLineCap(StrokeLineCap .ROUND )
96+ result.getChildren.addAll(lines : _* )
97+ result
98+ }
99+
100+ private def createUpImage : Node = {
101+ val result = new Group ()
102+ val lines = Array (
103+ new Line (- 4 , 2 , 0 , - 2 ),
104+ new Line (0 , - 2 , 4 , 2 ),
105+ )
106+ for (line <- lines)
107+ line.setStrokeLineCap(StrokeLineCap .ROUND )
108+ result.getChildren.addAll(lines : _* )
109+ result
110+ }
111+
60112 def activate (): Unit = {
61113 Platform .runLater(() => {
62- this .requestFocus()
63114 val selRange = editor.selectionProperty().getValue
64115 if (selRange.getLength > 0 ) {
65- startIndex = selRange.getStart
66- endIndex = selRange.getEnd
116+ startPos = selRange.getStart
117+ endPos = selRange.getEnd
67118 } else {
68- startIndex = 0
69- endIndex = - 1
119+ startPos = 0
120+ endPos = - 1
70121 }
122+ fullText = null
123+ updateText()
71124 findTextField.setText(" " )
72125 findTextField.requestFocus()
73126 })
74127 }
75128
76- private def doFindText (start : Int ): Boolean = {
77- if (searchText != null && searchText != " " ) {
78- val s : String = editor.textProperty().getValue
79- val index = s.indexOf(searchText, start)
80- if (index >= start) {
81- editor.selectRange(index, index + searchText.length)
82- currentIndex = index
129+ private def hasUppercase (s : String ): Boolean = {
130+ for (ch <- s)
131+ if (ch.isUpper)
83132 return true
133+ false
134+ }
135+
136+ private def doFindAll (): Array [Int ] =
137+ if (searchText != null && searchText != " " ) {
138+ val s : String = if (hasUppercase(searchText)) fullText else fullTextLC
139+ val result = collection.mutable.ArrayBuffer [Int ]()
140+ val lastIndex =
141+ if (endPos == - 1 )
142+ s.length - 1
143+ else
144+ endPos - searchText.length
145+ var idx = s.indexOf(searchText, startPos)
146+ while (startPos <= idx && idx <= lastIndex) {
147+ result += idx
148+ idx = s.indexOf(searchText, idx + 1 )
84149 }
150+ result.toArray
151+ } else
152+ Array ()
153+
154+ private def selectEntry (idx : Int ): Unit =
155+ if (idx < 0 && occurrences.nonEmpty)
156+ selectEntry(idx + occurrences.length)
157+ else if (occurrences.nonEmpty && searchText != " " ) {
158+ currentIndex =
159+ if (occurrences.length > 1 )
160+ idx % occurrences.length
161+ else
162+ 0
163+ val pos = occurrences(currentIndex)
164+ editor.selectRange(pos, pos + searchText.length)
165+ countLabel.setText(" %d/%d" .format(currentIndex + 1 , occurrences.length))
166+ } else {
167+ if (endPos > startPos)
168+ editor.selectRange(startPos, endPos)
169+ else
170+ editor.selectRange(startPos, startPos)
171+ countLabel.setText(" 0/0" )
172+ currentIndex = - 1
85173 }
86- if (endIndex > startIndex)
87- editor.selectRange(startIndex, endIndex)
88- else
89- editor.selectRange(startIndex, startIndex)
90- currentIndex = - 1
91- false
174+
175+ private def nextIndexAfterPos : Int = {
176+ val curPos = editor.selectionProperty().getValue.getStart
177+ for ((pos, i) <- occurrences.zipWithIndex)
178+ if (curPos <= pos)
179+ return i
180+ 0
92181 }
93182
94183 protected def findFirst (searchText : String ): Unit = {
95184 if (this .searchText == null || ! searchText.startsWith(this .searchText))
96- currentIndex = startIndex
185+ currentIndex = startPos
97186 this .searchText = searchText
98- doFindText(currentIndex)
187+ occurrences = doFindAll()
188+ if (occurrences.nonEmpty)
189+ selectEntry(nextIndexAfterPos)
190+ else
191+ selectEntry(0 )
99192 }
100193
101- protected def findNext (): Unit =
102- if (currentIndex >= 0 ) {
103- if (! doFindText(currentIndex + 1 ))
104- doFindText(startIndex)
105- }
194+ protected def findNext (): Unit = {
195+ selectEntry(currentIndex + 1 )
196+ }
197+
198+ protected def findPrev (): Unit = {
199+ selectEntry(currentIndex - 1 )
200+ }
201+
202+ protected def updateText (): Unit = {
203+ fullText = editor.textProperty().getValue
204+ fullTextLC = fullText.toLowerCase
205+ }
106206}
0 commit comments