When using a DefaultCellEditor in a JTable, there seem to be two different editing modes:
Single clicking on a cell does not display the editor component, but (for String/Object classes) one can still type in the selected cell and thereby edit its value.
Double clicking on a cell displays the editor component and one can edit the selected cell's value as expected.
Why is this? One would think that editing a JTable cell's value would always involve the same UI behavior.
Technically, double clicking a cell and typing in a cell (which supports text editing) essentially boils down to the same thing. You can actually double click a text editable cell to start the editing process
Cell editability comes to down to two factors, the result of TableModel#isCellEditable and TableCellEditor#isCellEditable, when these two methods return true a cell can be placed into edit mode.
A text editable cell is, generally, a special case, where the cell can be edited by typing in the cell while it has focus and double clicking (and pressing F2 in most cases).
The case for providing more than one mechanism for initializing the editing process will come down to decisions made over usability and existing conventions across multiple platforms. It's likely that some attempt was made to mix expectations where possible so users of different platforms could feel more comfortable with the process, but this is simply speculation.
The problem is, even amongst users of a single platform, there are different expectations about how something like this works, so rather the supply a single trigger, the system has been designed to allow for multiple triggers, where it would be applicable.
For example, it wouldn't make sense for a cell containing an image to allow the user to edit the cell simply by typing in it.
Updated
If you take a look at the isCellEdtiable method for DefaultCellEditor...
public boolean isCellEditable(EventObject anEvent) {
if (anEvent instanceof MouseEvent) {
return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
}
return true;
}
You will note that the only event that actually stops a cell from entering edit mode is a MouseEvent, but only when the number of mouse clicks is less than clickCountToStart, which is set to 2 when using a JTextField as the editor, otherwise it's 1
Related
I want JTable column to expand in width if a user enters too many characters into a cell so that content should remain visible. I do not want to use a keylogger as this is not clean, there could be other means of editing a cell.
It seems I can only react in the listeners either at the beginning of an edit or at its end. Can I also react to the editing in between? Or is there maybe something built in? I would have guessed this is a standard task.
I managed to listen to any editing event by using a DocumentListener for each cell.
It indeed works to resize the table during these events - however, if I do so and the column changed in size, the cell I am editing is suddenly colored in blue, as if I have just selected the column with the mouse. The cell however remains in editing mode.
I try now to get rid of the blue color by firing some more events. I hope the whole thing does not crash at some point.
Anyway, if I indeed follow this approach, I need to calcuate the required column size based on the characters typed into my cell - a difficult task if I use non-monospaced fonts. Any ideas on that?
I want to do plain UI testing (i.e., not using SWTBot or other UI test frameworks) of NatTable contents.
My approach is to create a shell, add my custom NatTable and then access the cell and check its contents (data value, config label etc.):
// Note: this is Xtend code
#Before
def void setup()
{
shell = new Shell(Display.getCurrent)
shell.layout = new FillLayout
parent = new Composite(shell, SWT.NONE)
parent.layout = new GridLayout
fixture = new MyNatTableViewer(parent) // this is my custom nattable impl under test
shell.pack
shell.visible = true
}
#Test
def void testLabel()
{
assertCellLabel(2, 2, "test-label");
}
def assertCellLabel(int row, int col, String expected)
{
val labels = parameterTable.getCellByPosition(col, row)?.configLabels
assertThat(labels).describedAs("Labels for row " + row + " col " + col).isNotNull
assertThat(labels.labels).describedAs("Labels for row " + row + " col " + col).contains(expected)
}
To test my other components it was enough to just create the shell and the parent composite; packing and setting visible was not required for my tests to work.
Yet, with NatTable, getCellByPosition() returns null if the cell is not visible - so I added the code to pack and set the shell visible. This works for small tables (with 2 rows and a few columns).
Sadly, it does not work for large tables. I suspect this is because the viewport layer does not create cells which are not in the visible area (which is, I know, the strength of NatTable - that it only creates the required structures on demand). This is, of course, desired for normal runtime behavior.
But is there a(nother) way to get the cell in a guaranteed way (in other words, can I make the NatTable/ViewportLayer believe that the cell is visible so I don't get null as long as the cell exists content-wise?)
I could, of course, test my label accumulators, data providers etc. directly, but I wanted to approach this more from a black-box point of view here.
That question is contradictory in itself. You are asking for a black box approach for testing NatTable, but you want to change the behavior of NatTable on testing. That is not a black box approach!
If you really want to test with a black box approach, you need to ensure that the cell is rendered. This can be done by triggering scrolling, e.g. by executing the ShowCellInViewportCommand. That is the real black box approach, because returning null for a non-visible cell is the correct result.
If you need something in between a real black box approach and an approach that makes use of internal knowledge (which you are asking for) you have to ways to get there.
Operate on a layer below the ViewportLayer. Typically the SelectionLayer can be used. But of course this doesn't need to mean anything, because the layer stack can differ from setup to setup. The ViewportLayer is the one that introduces the virtual nature to a NatTable and the scrolling ability. It avoids the access to the underlying layers. So asking one of these will return the value you expect.
Disable the ViewportLayer by executing the TurnViewportOffCommand. This is basically a hack and could trigger additional things in the back that you might not want. But I have seen that suggestion in other contexts and therefore want to name it here. I don't suggest to use it anyway!
Note that both approaches are more like hacks when we are talking about black box testing, because you are making assumptions to the composition. They can not be applied in general because of the various configuration abilities.
Regarding the hidden question about why you need to set the Shell visible. Well basically because the SWT events for painting and resizing need to be triggered in order to start the size calculations and printing of NatTable correctly according to the Shell state. In our examples (which are also plain SWT) we call Shell#open().
And as a last comment on your implementation, I don't understand why you are sub-classing NatTable. Our API was never intended to do that. I suppose you do this to do some static pre-configuration e.g. the layer stack. But personally I don't like that approach. Everytime someone extends our classes to override some internal methods it ends up in questions or bug reports because the behavior changes. But I think that is generally an issue of an open API to give developers the most possible flexibility on customization.
I am also trying to read data from invisible cells of nattable. I am trying to use ShowCellInViewportCommand as follows:
widget.doCommand(new ShowCellInViewportCommand(gridlayer.getBodyLayer(), column, row));
//where row is say 50 and column is 20 and the cell is invisible.
I also tried,
widget.doCommand(new ShowRowInViewportCommand(widget.getLayer(), row));
//here the default value given by nattable.getLayer is passed
widget corrosponds to nattable instance.
After the call, nothing happens in UI. The cell does not get displayed.
Do I need to do anything else?
How should I go to read invisible cells of nattable.
You can go into a cell in a Jtable either by clicking on it, or by going into it using cursor keys/tabs. With defaultCellEditor and a JtextField if you go in using cursor keys the caret is put at the end of the existing text field, whereas if you double click the field it will highlight the last word.
Whereas spreadsheets seem to work (such as Open Office Calc) the same way for a double clcking, but if you tab into the field and start editing the field is cleared and the first character pressed becomes the first value in the field and so on.
I want my app to work the same way as the spreadsheet. By subclassing DefaultCellEditor and adding
final Caret caret = editField.getCaret();
caret.setDot(0);
editField.setText("");
I can get it to work how I want when tabbing but it also clears the field on double click which I dont want.
So please how can I determine if cell editing has been triggered by keyboard or mouse ?
override the isCellEditable(EventObject anEvent) method too.
So that you can capture the event which is going to trigger (or not) the table edition and act the way you want
I am working with a JTreeTable as described in an old article at: Sun Developer Network
Here are the same files but slightly adjusted for java 6:
http://edtaylor80.angelfire.com
If you run this little example program you will find that selection works as expected to start with, entire rows are selected when you click a random cell. This behaviour is desired. However, as soon as a node is expanded the behaviour changes, now it is only possible to select a row by clicking at the actucal node (name). I still want to be able to select an entire row by clicking a random cell. How can I modify the source code to accomplish this?
Before you click into the first column to open a node, there is no cell editor for the JTable. Once you perform that operation, the table has an active cell editor, which is an instance of the AbstractCellEditor that comes as part of the JTreeTable example source. In the implementation therein, you will find this:
public boolean shouldSelectCell(EventObject anEvent) { return false; }
This gets called by the BasicTableUI when it determines whether to adjust selection or not. As you can see, it will always return false. This is why, once you open a node, you will see this odd selection behavior.
While on the topic of tree tables, I recommend that you check out NetBeans' Outline. It is an easy to use implementation, much less convoluted than the JTreeTable example from Sun. You can find links and a demo in this post.
The desired behavior is akin to the mirrored text editing field provided in Excel when a given cell is selected, allowing more space to view the contents of the cell. I have a JTable with 5 columns and n rows. Column 2 holds expressions that can be arbitrarily long, thus I'd like to provide a separate JTextField to work with for editing the contents of the expression cell per row. The other fields are directly editable in the table. When the user clicks on a field in column 2, however, I want to send them to the text field. Any contents preexisting in the cell should be appear in the text field and additional edits in the text field should be mirrored in the table cell. Likewise, if someone double-clicks on the cell and edits it directly, I want those changes reflected in the text field. Thus, the user can choose to edit in either space and both are updated. Ideally, they are updated per keystroke, but update upon hitting return is acceptable.
So, far I've got the JTable, TableModel, TableModelListener, JTextField, ListSelectionListener, and AbstractAction, working together to provide most of the functionality described above. I'm missing the reflection of direct table cell edits to the text field and per-keystoke updates.
Are their ideas on how best to construct this behavior?
Well, if you want to get data from the table to the cell then you add the code to your TableModel's setValueAt() function, which should run when the user changes the content in an editable cell. I don't think that will update per-keystroke though.
If you want to move data from the textbox to the table cell use code like this
myJTextField.getDocument().addDocumentListener(new MyDocumentListener());
Where MyDocumentListener is an implementation of the javax.swing.event.DocumentListener interface
That will get you per-keystroke updates from the box to the table. But for the other way around it's a bit trickier.
There are two ways you might be able to go about doing it
1) Add a key listener to the table, and when the user starts typing check to see what table element is active, and intercept keystrokes as they type. That's kind of messy, though.
2) Another option might be to try to grab or replace the component that the table is using to actually let the user make the changes. I think that JTable actually allows you to change the editor component if you dig around.