Skip to content

Commit 1cc5caf

Browse files
authored
Merge pull request #11 from processing/input-handler-fix
Input Handler
2 parents 8da4a88 + 5ae5e09 commit 1cc5caf

File tree

1 file changed

+354
-7
lines changed

1 file changed

+354
-7
lines changed
Lines changed: 354 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,371 @@
11
package processing.p5js
22

3+
import processing.app.Preferences
34
import processing.app.syntax.InputHandler
45
import processing.app.syntax.PdeInputHandler
56
import java.awt.event.KeyEvent
7+
import java.util.*
68

7-
class p5jsInputHandler(editor: p5jsEditor): PdeInputHandler(editor) {
8-
init{
9+
class p5jsInputHandler(editor: p5jsEditor) : PdeInputHandler(editor) {
10+
init {
911
this.addKeyBinding("ENTER", InputHandler.INSERT_BREAK)
1012
this.addKeyBinding("TAB", InputHandler.INSERT_TAB)
1113
}
1214

13-
override fun handlePressed(event: KeyEvent?): Boolean {
14-
val c = event!!.keyChar
15-
val code = event.keyCode
15+
/**
16+
* Intercepts key pressed events for JEditTextArea. Shamelessly stolen from Java Mode
17+
*
18+
*
19+
* Called by JEditTextArea inside processKeyEvent(). Note that this
20+
* won't intercept actual characters, because those are fired on
21+
* keyTyped().
22+
* @return true if the event has been handled (to remove it from the queue)
23+
*/
24+
override fun handlePressed(event: KeyEvent): Boolean {
25+
val c = event.getKeyChar()
26+
val code = event.getKeyCode()
27+
28+
val sketch = editor.getSketch()
29+
val textarea = editor.getTextArea()
30+
31+
if (event.isMetaDown()) {
32+
//if ((event.getModifiers() & InputEvent.META_MASK) != 0) {
33+
//event.consume(); // does nothing
34+
return false
35+
}
1636

1737
if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_TAB) ||
1838
(code == KeyEvent.VK_ENTER) || ((c.code >= 32) && (c.code < 128))
1939
) {
20-
editor.sketch.isModified = true
40+
sketch.setModified(true)
41+
}
42+
43+
if ((code == KeyEvent.VK_UP) && event.isControlDown()) {
44+
//((event.getModifiers() & InputEvent.CTRL_MASK) != 0)) {
45+
// back up to the last empty line
46+
val contents = textarea.getText().toCharArray()
47+
//int origIndex = textarea.getCaretPosition() - 1;
48+
val caretIndex = textarea.getCaretPosition()
49+
50+
var index: Int = calcLineStart(caretIndex - 1, contents)
51+
//System.out.println("line start " + (int) contents[index]);
52+
index -= 2 // step over the newline
53+
//System.out.println((int) contents[index]);
54+
var onlySpaces = true
55+
while (index > 0) {
56+
if (contents[index].code == 10) {
57+
if (onlySpaces) {
58+
index++
59+
break
60+
} else {
61+
onlySpaces = true // reset
62+
}
63+
} else if (contents[index] != ' ') {
64+
onlySpaces = false
65+
}
66+
index--
67+
}
68+
// if the first char, index will be -2
69+
if (index < 0) index = 0
70+
71+
//if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
72+
if (event.isShiftDown()) {
73+
textarea.setSelectionStart(caretIndex)
74+
textarea.setSelectionEnd(index)
75+
} else {
76+
textarea.setCaretPosition(index)
77+
}
78+
event.consume()
79+
80+
// return true;
81+
} else if ((code == KeyEvent.VK_DOWN) && event.isControlDown()) {
82+
//((event.getModifiers() & InputEvent.CTRL_MASK) != 0)) {
83+
val contents = textarea.getText().toCharArray()
84+
val caretIndex = textarea.getCaretPosition()
85+
86+
var index = caretIndex
87+
var lineStart = 0
88+
var onlySpaces = false // don't count this line
89+
while (index < contents.size) {
90+
if (contents[index].code == 10) {
91+
if (onlySpaces) {
92+
index = lineStart // this is it
93+
break
94+
} else {
95+
lineStart = index + 1
96+
onlySpaces = true // reset
97+
}
98+
} else if (contents[index] != ' ') {
99+
onlySpaces = false
100+
}
101+
index++
102+
}
103+
104+
//if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
105+
if (event.isShiftDown()) {
106+
textarea.setSelectionStart(caretIndex)
107+
textarea.setSelectionEnd(index)
108+
} else {
109+
textarea.setCaretPosition(index)
110+
}
111+
event.consume()
112+
113+
// return true;
114+
} else if (c.code == 9) {
115+
//if ((event.getModifiers() & InputEvent.SHIFT_MASK) != 0) {
116+
if (event.isShiftDown()) {
117+
// if shift is down, the user always expects an outdent
118+
// https://github.com/processing/processing/issues/497
119+
editor.handleOutdent()
120+
} else if (textarea.isSelectionActive()) {
121+
editor.handleIndent()
122+
} else if (Preferences.getBoolean("editor.tabs.expand")) {
123+
val tabSize = Preferences.getInteger("editor.tabs.size")
124+
textarea.setSelectedText(this.spaces(tabSize))
125+
event.consume()
126+
} else { // !Preferences.getBoolean("editor.tabs.expand")
127+
textarea.setSelectedText("\t")
128+
event.consume()
129+
}
130+
} else if (code == 10 || code == 13) { // auto-indent
131+
if (Preferences.getBoolean("editor.indent")) {
132+
val contents = textarea.getText().toCharArray()
133+
val tabSize = Preferences.getInteger("editor.tabs.size")
134+
135+
// this is the previous character
136+
// (i.e. when you hit return, it'll be the last character
137+
// just before where the newline will be inserted)
138+
val origIndex = textarea.getCaretPosition() - 1
139+
140+
// if the previous thing is a brace (whether prev line or
141+
// up farther) then the correct indent is the number of spaces
142+
// on that line + 'indent'.
143+
// if the previous line is not a brace, then just use the
144+
// identical indentation to the previous line
145+
146+
// calculate the amount of indent on the previous line
147+
// this will be used *only if the prev line is not an indent*
148+
var spaceCount: Int = calcSpaceCount(origIndex, contents)
149+
150+
// If the last character was a left curly brace, then indent.
151+
// For 0122, walk backwards a bit to make sure that the there isn't a
152+
// curly brace several spaces (or lines) back. Also moved this before
153+
// calculating extraCount, since it'll affect that as well.
154+
var index2 = origIndex
155+
while ((index2 >= 0) &&
156+
Character.isWhitespace(contents[index2])
157+
) {
158+
index2--
159+
}
160+
if (index2 != -1) {
161+
// still won't catch a case where prev stuff is a comment
162+
if (contents[index2] == '{') {
163+
// intermediate lines be damned,
164+
// use the indent for this line instead
165+
spaceCount = calcSpaceCount(index2, contents)
166+
spaceCount += tabSize
167+
}
168+
}
169+
170+
// now before inserting this many spaces, walk forward from
171+
// the caret position and count the number of spaces,
172+
// so that the number of spaces aren't duplicated again
173+
var index = origIndex + 1
174+
var extraCount = 0
175+
while ((index < contents.size) &&
176+
(contents[index] == ' ')
177+
) {
178+
//spaceCount--;
179+
extraCount++
180+
index++
181+
}
182+
var braceCount = 0
183+
while ((index < contents.size) && (contents[index] != '\n')) {
184+
if (contents[index] == '}') {
185+
braceCount++
186+
}
187+
index++
188+
}
189+
190+
// Hitting return on a line with spaces *after* the caret
191+
// can cause trouble. For 0099, was ignoring the case, but this is
192+
// annoying, so in 0122 we're trying to fix that.
193+
spaceCount -= extraCount
194+
195+
if (spaceCount < 0) {
196+
// for rev 0122, actually delete extra space
197+
//textarea.setSelectionStart(origIndex + 1);
198+
textarea.setSelectionEnd(textarea.getSelectionStop() - spaceCount)
199+
textarea.setSelectedText("\n")
200+
textarea.setCaretPosition(textarea.getCaretPosition() + extraCount + spaceCount)
201+
} else {
202+
val insertion = "\n" + this.spaces(spaceCount)
203+
textarea.setSelectedText(insertion)
204+
textarea.setCaretPosition(textarea.getCaretPosition() + extraCount)
205+
}
206+
207+
// not gonna bother handling more than one brace
208+
if (braceCount > 0) {
209+
val sel = textarea.getSelectionStart()
210+
// sel - tabSize will be -1 if start/end parens on the same line
211+
// https://download.processing.org/bugzilla/484.html
212+
if (sel - tabSize >= 0) {
213+
textarea.select(sel - tabSize, sel)
214+
val s: String = this.spaces(tabSize)
215+
// if these are spaces that we can delete
216+
if (s == textarea.getSelectedText()) {
217+
textarea.setSelectedText("")
218+
} else {
219+
textarea.select(sel, sel)
220+
}
221+
}
222+
}
223+
} else {
224+
// Enter/Return was being consumed by somehow even if false
225+
// was returned, so this is a band-aid to simply fire the event again.
226+
// https://download.processing.org/bugzilla/1073.html
227+
textarea.setSelectedText(c.toString())
228+
}
229+
// mark this event as already handled (all but ignored)
230+
return true
231+
232+
// return true;
233+
} else if (c == '}') {
234+
if (Preferences.getBoolean("editor.indent")) {
235+
// first remove anything that was there (in case this multiple
236+
// characters are selected, so that it's not in the way of the
237+
// spaces for the auto-indent
238+
if (textarea.getSelectionStart() != textarea.getSelectionStop()) {
239+
textarea.setSelectedText("")
240+
}
241+
242+
// if this brace is the only thing on the line, outdent
243+
val contents = textarea.getText().toCharArray()
244+
// index to the character to the left of the caret
245+
val prevCharIndex = textarea.getCaretPosition() - 1
246+
247+
// backup from the current caret position to the last newline,
248+
// checking for anything besides whitespace along the way.
249+
// if there's something besides whitespace, exit without
250+
// messing any sort of indenting.
251+
var index = prevCharIndex
252+
var finished = false
253+
while ((index != -1) && (!finished)) {
254+
if (contents[index].code == 10) {
255+
finished = true
256+
index++
257+
} else if (contents[index] != ' ') {
258+
// don't do anything, this line has other stuff on it
259+
return false
260+
} else {
261+
index--
262+
}
263+
}
264+
if (!finished) return false // brace with no start
265+
266+
val lineStartIndex = index
267+
268+
val pairedSpaceCount: Int = calcBraceIndent(prevCharIndex, contents) //, 1);
269+
if (pairedSpaceCount == -1) return false
270+
271+
textarea.setSelectionStart(lineStartIndex)
272+
textarea.setSelectedText(this.spaces(pairedSpaceCount))
273+
274+
// mark this event as already handled
275+
event.consume()
276+
return true
277+
}
278+
}
279+
return false
280+
}
281+
282+
/**
283+
* Return the index for the first character on this line.
284+
*/
285+
fun calcLineStart(index: Int, contents: CharArray): Int {
286+
// backup from the current caret position to the last newline,
287+
// so that we can figure out how far this line was indented
288+
/*int spaceCount = 0;*/
289+
var index = index
290+
var finished = false
291+
while ((index != -1) && (!finished)) {
292+
if ((contents[index].code == 10) ||
293+
(contents[index].code == 13)
294+
) {
295+
finished = true
296+
//index++; // maybe ?
297+
} else {
298+
index-- // new
299+
}
300+
}
301+
// add one because index is either -1 (the start of the document)
302+
// or it's the newline character for the previous line
303+
return index + 1
304+
}
305+
306+
/**
307+
* Calculate the number of spaces on this line.
308+
*/
309+
protected fun calcSpaceCount(index: Int, contents: CharArray): Int {
310+
var index = index
311+
index = calcLineStart(index, contents)
312+
313+
var spaceCount = 0
314+
// now walk forward and figure out how many spaces there are
315+
while ((index < contents.size) && (index >= 0) &&
316+
(contents[index++] == ' ')
317+
) {
318+
spaceCount++
319+
}
320+
return spaceCount
321+
}
322+
323+
324+
/**
325+
* Walk back from 'index' until the brace that seems to be
326+
* the beginning of the current block, and return the number of
327+
* spaces found on that line.
328+
*/
329+
protected fun calcBraceIndent(index: Int, contents: CharArray): Int {
330+
// now that we know things are ok to be indented, walk
331+
// backwards to the last { to see how far its line is indented.
332+
// this isn't perfect cuz it'll pick up commented areas,
333+
// but that's not really a big deal and can be fixed when
334+
// this is all given a more complete (proper) solution.
335+
var index = index
336+
var braceDepth = 1
337+
var finished = false
338+
while ((index != -1) && (!finished)) {
339+
if (contents[index] == '}') {
340+
// aww crap, this means we're one deeper
341+
// and will have to find one more extra {
342+
braceDepth++
343+
//if (braceDepth == 0) {
344+
//finished = true;
345+
//}
346+
index--
347+
} else if (contents[index] == '{') {
348+
braceDepth--
349+
if (braceDepth == 0) {
350+
finished = true
351+
}
352+
index--
353+
} else {
354+
index--
355+
}
21356
}
22-
return super.handlePressed(event)
357+
// never found a proper brace, be safe and don't do anything
358+
if (!finished) return -1
359+
360+
// check how many spaces on the line with the matching open brace
361+
//int pairedSpaceCount = calcSpaceCount(index, contents);
362+
return calcSpaceCount(index, contents)
363+
}
364+
365+
366+
private fun spaces(count: Int): String {
367+
val c = CharArray(count)
368+
Arrays.fill(c, ' ')
369+
return String(c)
23370
}
24371
}

0 commit comments

Comments
 (0)