I am working on a dictionary app that displays word entries like this:
The word entry is made up of five JLabels. The JLabels EN, n and the star have tooltip text that explains what the abbreviation or symbol means. This works correctly when the word entry is placed in a regular JPanel, but if it is placed in a JList item the tooltip text is not displayed- presumably overwritten by the JList's (non-existent) tooltip.
I have seen answers like this, but they only add a tooltip for the JList item, not for individual JComponents with the item.
Is there any way to, for example, change the Z-order so that the tooltips for the JLabels is visible?
The word entry is made up of five JLabels.
You should NOT be storing Swing compnents in the ListModel. The point of using renderers is to store the data in the model and then use a renderer to display the data.
So in your case you need to create a custom object with 8 properties. 5 properties for the text to be displayed in the labels and 3 properties for the tooltip text. Then your renderer will be a JPanel with 5 labels. In the rendering code you simply set the text of each of the 5 labels with the data from your custom object. You would also set the tooltip text.
Next you will need to override the getToolTipText(MouseEvent) method of the JList. The default implementation of this code gets the renderer and then just returns the tootip of the renderer. This is simple because the renderer is usually just a single JLabel.
In your case the logic will be slightly more complicated because you have child components in the renderer so you will need to get the tooltip of the child component.
Following is the current code from the getToolTipText(...) method:
public String getToolTipText(MouseEvent event) {
if(event != null) {
Point p = event.getPoint();
int index = locationToIndex(p);
ListCellRenderer<? super E> r = getCellRenderer();
Rectangle cellBounds;
if (index != -1 && r != null && (cellBounds =
getCellBounds(index, index)) != null &&
cellBounds.contains(p.x, p.y)) {
ListSelectionModel lsm = getSelectionModel();
Component rComponent = r.getListCellRendererComponent(
this, getModel().getElementAt(index), index,
lsm.isSelectedIndex(index),
(hasFocus() && (lsm.getLeadSelectionIndex() ==
index)));
if(rComponent instanceof JComponent) {
MouseEvent newEvent;
p.translate(-cellBounds.x, -cellBounds.y);
newEvent = new MouseEvent(rComponent, event.getID(),
event.getWhen(),
event.getModifiers(),
p.x, p.y,
event.getXOnScreen(),
event.getYOnScreen(),
event.getClickCount(),
event.isPopupTrigger(),
MouseEvent.NOBUTTON);
String tip = ((JComponent)rComponent).getToolTipText(
newEvent);
if (tip != null) {
return tip;
}
}
}
}
return super.getToolTipText();
}
I thank the code you need to change would be in this area:
p.translate(-cellBounds.x, -cellBounds.y);
newEvent = new MouseEvent(rComponent, event.getID(),
This translates the mouse point from the JList to the renderer. So now I think you just need to modify the logic to get the label at the translated point and then get the labels tool tip.
So the new code might be something like:
p.translate(-cellBounds.x, -cellBounds.y);
Component label = rComponent.getComponentAt(p);
if (label == null) return super.getToolTipText();
newEvent = new MouseEvent(label, event.getID(),
You are confused. You don't add Components to a JList, you use a ListRenderer.
So you have two choices: You can either build your own custom tooltip for the JList, or create your own version of JList
Related
This is how the program looks:
This is how I want it to look:
As you can see in the picture I have tried a bit and learned that I need to use ListCellRenderer, but the problem is i have created 2 custom png files
checked.png and
unchecked.png
when I click daily goals #1 it should give state = true and checked.png should appear and stay checked unless I click it again. Unchecked.png could be standard on the jList column.
I also want to place my checkbox 1 cm to the left of the end of the row (padding) not sure hows its done in java sadly. (You'll understand better by looking at the picture)
After looking through some guides I have learned that the only way to add extra stuff to a JList column is by using ListCellRenderer. I have tried quite a while with no success so thought of asking others. Does anyone have any ideas on how to do this?
The thought was to get it to work then display in a JTable by changing the Jtable column to Daily goals and displaying X to indicate the goal was achieved. But I think I should be able to do this, The main question is the custom checkbox implementation.
You can have two types of checkboxes to be used as jlist cell renderers, one for selected cells, another for unselected.
Use ImageIcon to decorate the checkbox with your images.
In your jlist cell render you need to have logic to return the intended checkbox to render that list cell.
Note to override the text in the checkbox to the actual list cell value
public class TestFrame extends JFrame {
ImageIcon iconChecked = new ImageIcon(TestFrame.class.getResource("checked.png"));
ImageIcon iconUnchecked = new ImageIcon(TestFrame.class.getResource("unchecked.png"));
JList jList = new JList(new Object[]{"ABC", "123"});
public TestFrame() {
this.add(jList);
jList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
jList.setCellRenderer(new ListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
for (int i : list.getSelectedIndices()) {
if (index == i) {
JCheckBox checkBoxChecked = new JCheckBox(value.toString(), iconChecked);
return checkBoxChecked;
}
}
JCheckBox checkBoxUnchecked = new JCheckBox(value.toString(), iconUnchecked);
return checkBoxUnchecked;
}
});
}}
I am making a grid-style game/simulation based on bugs "sensing" and eating food. I am using a gridPane (called worldGrid) of labels to show the grid of bugs and food. This is obviously going to be constantly updated when a bug moves cells towards food etc.
I currently have a function updateGrid(int col, int row, String cellContent) which I want to replace the label at [row,col] with a label that has the new text in cellContent.
I have the follow which works
worldGrid.add(new Label(cellContent), row,col);
however im worried that that is just adding a label on top of the current label and obviously over 100 iterations of the simulation thats not ideal.
I have tried this before adding the label:
worldGrid.getChildren().remove(row,col);
However I then get an IllegalArgumentException when trying to do the add line.
Any ideas on how to do this? Or even better, any ideas on how best to show a constantly changing grid that will eventually use sprites instead of text?
The col/row provided by grid.add(node, col, row) (ATTENTION first comes col!) is only a layout constraint. This does not provide any means to access columns or rows like slots in a 2-dimensional array. So to replace a node, you have to know its object itself, e.g. remember them in a separate array.
Then you are able to call getChildren().remove(object)... e.g.:
GridPane grid = new GridPane();
Label first = new Label("first");
Label second = new Label("second");
grid.add(first, 1, 1);
grid.add(second, 2, 2);
second.setOnMouseClicked(e -> {
grid.getChildren().remove(second);
grid.add(new Label("last"), 2, 2);
});
box.getChildren().addAll(grid);
I agree with Jens-Peter but I would add that you can use GridPane's getColumnIndex and getRowIndex to obtain a particular node's location.
For example ( this is in a component which extends GridPane ):
// set the appropriate label
for (Node node : getChildren()) {
if (node instanceof Label
&& getColumnIndex(node) == column
&& getRowIndex(node) == row) {
((Label)node).setTooltip(new Tooltip(tooltip));
}
}
in other words, go through all the nodes in the GridPane and check for a match of the conditions you want, in this case row and column.
You called getChildren().remove() method directly will cause the gridpane to go out of sync with the constraints. When you add, it also setup the constraint for the node. Add clearConstraints() method.
How can I fetch a component from a JList, with the click location?
I have my own list cell renderer where I insert some panels and labels.
Now i want to get e.g. the label where the user clicked at.
I tried the method list.getComponentAt(evt.getPoint()); but it returns only the entire JList.
I've not tested this, but the basics would be...
Use JList#locationToIndex(Point) to get the index of the element at
the given point.
Get the "element" at the specified index (using
JList#getModel#getElementAt(int)).
Get the ListCellRenderer using JList#getCellRenderer.
Render the element and get it's Component representation
Set the renderer's bounds to the required cell bounds
Convert the original Point to the Components context
Use getComponentAt on the renderer...
Possibly, something like...
int index = list.locationToIndex(p);
Object value = list.getModel().getElementAt(int);
Component comp = listCellRenderer.getListCellRendererComponent(list, value, index, true, true);
comp.setBounds(list.getCellBounds(index, index));
Point contextPoint = SwingUtilities.convertPoint(list, p, comp);
Component child = comp.getComponentAt(contextPoint);
MadProgrammer's works fine as long as the user doesn't click outside a cell. If he does that, the index returned by locationToIndex() will be the last index's cell, so the converted point will be "under" the rendered component
To check if the user really clicked a cell you have to do:
int index = list.locationToIndex(p);
if (index > -1 && list.getCellBounds(index, index).contains(p)){
// rest of MadProgrammer solution
...
}
I would like to customize JTableHeader so it would offer serval actions (for example 2 buttons which one of them would sort column and second show properties of this column etc). Unfortunately it is not possible to set CellEditor for JTableHeader so i'm stuck with using mouse adapter. But maybe it is possible to dispatch event from this particular JTableHeader component so it will show up a popup menu which will contains all options i desire and it would dispatch event if option other than sorting would be chosen. This way standard JTable sorting operation will be available, along with my operations and it will maintain a decent visual apperance. So my question is - Is it possible and how it should be done.
In response to trashgod comment - i understand that you mean to treat defaultheader as an ordinary component and just use "add" function to add Components. It doesnt work well with JTableHeader. After reading trashgod example i wrote this:
private class mouseList extends MouseAdapter {
#Override
public void mouseClicked(MouseEvent e) {
TableColumnModel thisColumnModel = thisTable.getColumnModel();
int xCor = e.getX();
//int Cols = thisColumnModel.getColumnCount();
int thisColNum = thisColumnModel.getColumnIndexAtX(xCor);
int prevWidth=0;
for(int i = 0 ;i<thisColNum;i++)
{
prevWidth+=thisColumnModel.getColumn(i).getWidth();
}
int width = xCor-prevWidth;
/////////////////////////////////////////////////////////////////////////////////////
customHeader thisHeader = (customHeader)((JTableHeader)e.getSource()).getDefaultRenderer();
System.out.println(thisHeader.mainB.getText() + " text of thisHeader");
//////////////////////////////////////////////////
test thisTest = new test(null,false,thisHeader);
thisTest.setVisible(true);
///////////////////////////////////////////////////////////////
//System.out.println(width + " width of the header");
Object thisComp = thisHeader.getComponentAt(width, e.getY());
System.out.println(thisComp + "\n" + width + " + " + e.getY() +"\n" + thisHeader.getMainButton().getText());
((JTableHeader)e.getSource()).repaint();
if(thisComp instanceof JButton)
{
//System.out.println("sdfdsf");
String name = ((JButton)thisComp).getName();
if(name.equals("mainB"))
{
System.out.println("its working on main");
((JButton)thisComp).doClick(1000);
}else{
System.out.println("its working on menu");
((JButton)thisComp).doClick(1000);
}
}
((JTableHeader)e.getSource()).repaint();
}
}
MouseListener is applied to JTableHeader. HeaderRender is an extension of JPanel that contains 2 JButtons. Strange thing happens in line
Object thisComp = thisHeader.getComponentAt(width, e.getY());
When i left lines
test thisTest = new test(null,false,thisHeader);
thisTest.setVisible(true);
(This dialog shows selected component)
uncommented, function "getComponentAt" seems to work allmost fine (allmost because it never goes for else condition even when mouse is targeting second button, and it does not repaint clicked buttons[Strangely its repainting buttons in test dialog window]),otherwise it allways returns null object.
I dont know if it is important but i set Header renderer globally by invoking "setDefaultRenderer" on JTableHeader.
Im pretty much running out of ideas so i would appreciate any help.
This example shows the basic infrastructure, while this answer offers several important caveats regarding usability. This example shows how to change the RowFilter dynamically, but changing the RowSorter is similar. Both examples use JToggleButton to manage two states, but a JComboBox could be used to select from among more alternatives.
I have a JTable in my application. I have a custom renderer setup on the table (a JTextArea, with line-wrapping enabled) which allows for multi-line content. The contents of the JTable cells are expected to overflow the bounds of the cell in some cases. I want to do the following:
Instead of making the user drag the row border to resize the cell, I want the user to be able to double-click the row border, so when I detect this, I can automatically resize the height of the cell (height of the row) to show the entire contents of the cell.
My question is, what is the best way to detect a double-click on the row border? I have gotten this to work by setting up a MouseListener on the JTable with a mouseClicked method that looks like this:
public class MouseButtonInputListener extends MouseInputAdapter
{
private JTable fTable;
public MouseButtonInputListener(JTable parentTable)
{
fTable = parentTable;
}
#Override
public void mouseClicked(MouseEvent e)
{
// Some pre-processing here...
if(e.getClickCount() == 2 &&
fTable.getCursor().getType() == Cursor.N_RESIZE_CURSOR)
{
// Auto-adjust row height to fit contents...
}
}
}
While this works, i'm not very happy with the line:
fTable.getCursor().getType() == Cursor.N_RESIZE_CURSOR
Any suggestions on a better way to do this? Is the above a reliable approach on all platforms?
After some searching online, I found the following page:
http://java.sun.com/products/jlf/ed2/book/HIG.Behavior.html
amongst other things, it specifies mouse cursor behavior under different circumstances. According to the section on "Pointer Feedback", the cursor switches between a N_RESIZE_CURSOR and S_RESIZE_CURSOR depending on whether it is the upper or lower boundary of a component that is being hovered over. It's interesting to note that on 2 out of 3 platforms (Mac and Windows), these cursors are exactly the same. Anyway, it follows that my code should therefore read:
#Override
public void mouseClicked(MouseEvent e)
{
// Some pre-processing here. Determine row at mouse click location
int cursorType = fTable.getCursor().getType();
if(e.getClickCount == 2 &&
(cursorType == Cursor.N_RESIZE_CURSOR || cursorType == CURSOR.S_RESIZE_CURSOR))
{
// Resize row appropriately.
}
}
This will work on all platforms. Thanks for the inputs and comments everyone.