How to prevent JScrollPane arrow key handling from moving caret when Scroll Pane wraps Text Pane
Asked Answered
D

2

1

I have the following requirements:

I need a scrollable JTextPane. The user may type into this text pane, or text may be inserted into it that is not typed by the user. Think something like an IM window. Although the window must be scrollable to allow the user to review text previously typed, the caret should never move from its position at the end of the text. Any text entered by the user will always appear at the end.

In JTextPane, when the user scrolls with the scroll bar, the caret does not move. The viewport is adjusted. However, when the user presses the up and down arrow keys, the JTextPane caret moves with it (whether the window scrolls or not).

What I want is that an up arrow key would function the same as moving the scroll bar up with the mouse. The arrow keys should have nothing to do with the caret.

I have tried the following approaches, without success: 1) add a "No-op" action to the Keymap for my text pane class (using JTextPane.addKeymap() and Keymap.addActionForKeyStroke()). This stops the caret from moving but prevents the action from being passed on to the Scroll Pane to scroll the view. 2) remove the arrow keys from the keymap for my text pane class. This affects all JTextPanes in my application which is not what I want.

What I want is to add an action to my TextPane keymap that simply calls the ScrollPane action for Up and Down arrow.

What is the best way to accomplish this?

A possibility that occurs to me is to implement a KeyListener (which receives the key stroke before the keymap) to trap these keys and then to implement the scrolling by hand. But this would seem to require me to compute font sizes, etc. Is there an easier way?

The ideal thing would be if there was some way to "anchor" the caret to whatever the end of the text was.

Diablerie answered 20/8, 2012 at 16:13 Comment(0)
S
4

You're going to have to modify the KeyBindings

Try this to start with

InputMap im = textArea.getInputMap(WHEN_FOCUSED);
ActionMap am = textArea.getActionMap();

am.get("caret-down").setEnabled(false);
am.get("caret-up").setEnabled(false);

Now that you have that working, you need to worry about all these

selection-up = shift pressed UP
caret-next-word = ctrl pressed RIGHT
selection-previous-word = shift ctrl pressed LEFT
selection-up = shift pressed KP_UP
caret-down = pressed DOWN
caret-previous-word = ctrl pressed LEFT
caret-end-line = pressed END
selection-page-up = shift pressed PAGE_UP
caret-up = pressed KP_UP
delete-next = pressed DELETE
caret-begin = ctrl pressed HOME
selection-backward = shift pressed LEFT
caret-end = ctrl pressed END
delete-previous = pressed BACK_SPACE
selection-next-word = shift ctrl pressed RIGHT
caret-backward = pressed LEFT
caret-backward = pressed KP_LEFT
selection-forward = shift pressed KP_RIGHT
delete-previous = ctrl pressed H
unselect = ctrl pressed BACK_SLASH
insert-break = pressed ENTER
selection-begin-line = shift pressed HOME
caret-forward = pressed RIGHT
selection-page-left = shift ctrl pressed PAGE_UP
selection-down = shift pressed DOWN
page-down = pressed PAGE_DOWN
delete-previous-word = ctrl pressed BACK_SPACE
delete-next-word = ctrl pressed DELETE
selection-backward = shift pressed KP_LEFT
selection-page-right = shift ctrl pressed PAGE_DOWN
caret-next-word = ctrl pressed KP_RIGHT
selection-end-line = shift pressed END
caret-previous-word = ctrl pressed KP_LEFT
caret-begin-line = pressed HOME
caret-down = pressed KP_DOWN
selection-forward = shift pressed RIGHT
selection-end = shift ctrl pressed END
selection-previous-word = shift ctrl pressed KP_LEFT
selection-down = shift pressed KP_DOWN
insert-tab = pressed TAB
caret-up = pressed UP
selection-begin = shift ctrl pressed HOME
selection-page-down = shift pressed PAGE_DOWN
delete-previous = shift pressed BACK_SPACE
caret-forward = pressed KP_RIGHT
selection-next-word = shift ctrl pressed KP_RIGHT
page-up = pressed PAGE_UP
Slifka answered 20/8, 2012 at 20:33 Comment(8)
Is see two problems with this approach (I think - have not tried it).Diablerie
Is see two problems with this approach (I think - have not tried it). 1. The action map retrieved by JTextArea.getActionMap(), is, I believe, a REFERENCE to the action map for all JTextAreas that comes from the look and feel, so doing this would disable the command for all such components. 2. It is not enough to disable caret-up and caret-down. We also need to cause the key-press to implement "ScrollPane.unitScrollUp" and "ScrollPane.unitScrollDown". It seems that many of the standard look and feels haven't thought through the scenario where a no-mouse mode of operation is a requirement.Diablerie
Your first point is invalid. I use this technique all the time & it only ever effects the instance of the object in question. In my test, when I pressed up or down, the caret remaind in position, but I was able to scroll the text area, I don't know if this is the feature you want or not. Thirdly, with KeyListeners, you have no way of knowing, precisely where in the event chain you are been called (or if that chain is the same for all OSs/look & feels)Slifka
Well, I stand corrected then. I will have to try this. If this solution works, it's exactly what I was looking for and much better than the KeyListener kludge. I'd been down a similar path before, but I now understand the difference. I was REMOVING the actions from the action map, whereas you are just disabling it.Diablerie
Yeah, this is the bees' knees!Diablerie
@SteveCohen I thought it is pretty funky when I got it to work ;)Slifka
No, actually I was removing KeyStrokes from the InputMap. Instead I needed to be disabling Actions on the ActionMap. In all honesty, I had never seen the Action.setEnabled() API before!Diablerie
@SteveCohen I've been looking for away to to do this (remove input/actions) for some time, but this is such a neat little idea it's amazingSlifka
S
1

What if you let user place caret e.g. to let him select and copy some text?

I would add a DocumentFilter (or override insertString() method of the Document) and in all cases perform insert in the doc.getLength() position and resetting the caret to the doc.getLength() position after the insert.

Sarmentum answered 21/8, 2012 at 7:46 Comment(1)
Cut/Copy/Paste is not to be supported in this component, so this answer is not relevant to the case.Diablerie

© 2022 - 2024 — McMap. All rights reserved.