How to optimize displaying different JPanel depending on JTree selection - java

I have a JTree with several nodes and each node has a different associated JPanel I want to display to the user. I've made use of a custom TreeCellRenderer and my code currently works as is, but (I think) it's a little too clunky and I have concerns about it
1) If I just leave a tree node selected, my code will keep refreshing the JPanel over and over again. Once the selected node has displayed the JPanel once, I don't want it to again unless another node is selected in between.
2) If the user selects a different node (let's say node2) while my timer is running for node1, I don't want to bother displaying node1's JPanel since the user has navigated away from it.
Here is my code of interest:
Component ret = super.getTreeCellRendererComponent(tree, value,
selected, expanded, leaf, row, hasFocus);
DefaultMutableTreeNode entry = (DefaultMutableTreeNode)value;
// if the node is selected
if(sel)
{
// set the background of the node
setBackgroundNonSelectionColor(new Color(0x91, 0xC5, 0xFF));
// display the JPanel for the node
displayPanel(entry);
}
else
{
// if the node isn't selected then no background selection color
setBackgroundNonSelectionColor(Color.WHITE);
}
return ret;
displayPanel function:
// display the node's JPanel after sleeping for 1s
private void displayPanel(final DefaultMutableTreeNode entry)
{
Thread thr = new Thread(){
public void run(){
// sleep for 1000ms
CF.sleep("1000");
// display the panel for this node
CF.displayPanel(entry);
}
};
thr.start();
}
To be honest, DefaultTreeCellRenderer is still a little confusing to me. I'd appreciate any advice on how to make this run efficiently.

Maybe I'm missing something here but couldn't you just use a Tree Selection listener on the tree?
tree = new JTree(treeModel);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent arg0) {
//Code here to get selection and display panel
}
});

TreeCellRenderer are meant for "rendering" purposes. What is the representation of a given object? Is it a label? Is it a checkbox? Something more complex? Basically it is used to "print" the representation of each node of the JTree on the display. Rendering may occur many times, at unpredictable moments, and is therefore not a good place to listen for selection.
What you are looking for is a TreeSelectionListener which has a single method to implement and which will be triggered every time the selection of the JTree changes.
Add your listener with javax.swing.JTree.getSelectionModel().addTreeSelectionListener().
final JTree tree = ...;
tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
if (tree.getSelectionPath()!=null)
displayPanel((DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent());
}
});

Related

Why doesn't my Combobox update its color properly?

I am implementing a dark mode into my program and everything works just fine, except a Combobox, which doesn't want to change its color as I want.
(source: bilder-upload.eu)
So as you can see, the "popup" of the Combobox changes the color just fine, but the Combobox itself doesn't. Also the Foreground color of the Combobox changes, but the background not.
I guess, the Look and Feel might cause the issue.
In my main-class:
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
Where I change to Darkmode:
TeamInterface.userFilterComboBox.setBackground( darkBackgroundColor );
TeamInterface.userFilterComboBox.setForeground( fontColor );
SwingUtilities.updateComponentTreeUI( TeamInterface.userFilterComboBox );
I have to use the updateComponentTreeUI-Method, because otherwise the "popup" also remains white.
If I remove the look and feel in my main-class, the combobox looks good,as you can see in this picture,
(source: bilder-upload.eu)
but I doesn't want to get rid of the system look and feel, so I tried to manually edit the UI of the combobox to metal with this code :
userFilterComboBox.setUI( new MetalComboBoxUI() );
but.. the result is just awful, even thoe theoretically (at leats thats what I think) it should look the same as without look and feel
(source: bilder-upload.eu)
The Combobox not is a component only to the background and the foreground but is the complex component.
An example:JComboBox is composed to:
ArrowButton
List of itme
Border (and it have a color)
the item selected
So for change all you can add inside your UIManager, all constant or you can define a new UIComponent.
So an PersonalComboBoxUI can the following:
/**
* #contributor https://github.com/vincenzopalazzo
*/
public class PersonalComboBoxUI extends BasicComboBoxUI {
public static ComponentUI createUI (JComponent c) {
return new PersonalComboBoxUI ();
}
#Override
public void installUI (JComponent c) {
super.installUI (c);
JComboBox<?> comboBox = (JComboBox<?>) c;
comboBox.setBackground (UIManager.getColor ("ComboBox.background"));
comboBox.setForeground (UIManager.getColor ("ComboBox.foreground"));
comboBox.setBorder (UIManager.getBorder ("ComboBox.border"));
comboBox.setLightWeightPopupEnabled (true);
}
#Override
protected JButton createArrowButton () {
Icon icon = UIManager.getIcon ("ComboBox.buttonIcon");
JButton button;
if (icon != null) {
button = new JButton (icon);
}
else {
button = new BasicArrowButton (SwingConstants.SOUTH);
}
button.setOpaque (true);
button.setBackground (UIManager.getColor ("ComboBox.buttonBackground"));
button.setBorder (BorderFactory.createLineBorder(Color.black));
return button;
}
#Override
protected ListCellRenderer createRenderer() {
return new MaterialComboBoxRenderer();
}
}
You should be defined also the PersonalComboBoxRenderer
/**
* #contributor https://github.com/vincenzopalazzo
*/
public class PersonalComboBoxRenderer extends BasicComboBoxRenderer {
#Override
public Component getListCellRendererComponent (JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JComponent component = (JComponent) super.getListCellRendererComponent (list, value, index, isSelected, cellHasFocus);
component.setBorder (BorderFactory.createEmptyBorder (5, 5, 5, 5));
component.setForeground (UIManager.getColor ("ComboBox.foreground"));
component.setBackground (isSelected || cellHasFocus ?
UIManager.getColor("ComboBox.selectedInDropDownBackground") :
UIManager.getColor("ComboBox.background"));
return component;
}
}
ps: in this case, I'm using the UIManager.put("ComboBox.background", COLOR) for add and stratification inside the JComponent.
So I want to add two information regarding if you using the personal color inside the UIManager or the PersonalComboBoxUI the color should be defined with this code
Color PINK_400 = new ColorUIResource (236, 64, 122);
because when you go to remove look and feel the color couldn't remove but if you used ColorUIResource the look and feel should be removed correctly.
To finish, if you do not have needed the default look and feel, I want to suggest you use a library.
The material-UI-swing has a system theming for creating the personal timing in your app and the all theme is personalizable.
This is the repo vincenzoapalazzo/material-ui-swing and atarw/material-ui-swing are the same repository and the same developer so, the vincenzopalazzo/material-us-swing is the developer branch, an contains more fix and test.
An example of the library is
.
Ps: I am the designer of the MaterialTheming System.

JFace Drag&Drop items highlight

I'm having problems understanding how a TreeViewer's item is highlighted while a user is dragging an item.
Here's what happens: I start dragging the bottom item within the Treeviewer, and the items next to it highlight accordingly. The problem is, I can't get the highlighted item from the DragOver event. But if i drop the item from this position, the event in Drop method will have the "item" field holding the highlighted item. The tree's selection isn't changed when the highlight occurs
What i want to do: I want to change the image of the pointer according to the highlighted item. The problem is I don't know how to understand which one is highlighted. Another mistery to me is that in the Drop method the highlighted item will be the target of the drop (the secont Field from the top, in this case). I do not want to use SWT.FULL_SELECTION
Here's the image:
Source snippets (what i want is the functionality of DragOver in cases when I'm not directly hovering over an item)
final DropTarget valuesTarget = new DropTarget(tree, DND.DROP_MOVE);
valuesTarget.addDropListener(new DropTargetAdapter()
#Override
public void dragOver(DropTargetEvent event)
{
if (transfer.isSupportedType(event.currentDataType))
{
final DropTarget target = (DropTarget)event.widget;
final Tree tree = (Tree)target.getControl();
final Point relativeDropPoint = getRelativeDropPoint(event);
final TreeItem targetItem = tree.getItem(relativeDropPoint);
if (targetItem != null)
{
event.feedback =
DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
if (event.item.getData() instanceof NotAcceptableClass)
{
event.detail = DND.DROP_NONE;
}
}
}
}
private Point getRelativeDropPoint(DropTargetEvent event)
{
final Tree tree = (Tree)((DropTarget)event.widget).getControl();
final Point tableLocation = tree.toDisplay(0, 0);
return new Point(event.x - tableLocation.x, event.y
- tableLocation.y);
}
Take the TreeItem directly from DropTargetEvent.item.
If you would be using JFace TreeViewer with associated content and label providers then you could use ViewerDropAdapter, which would take care of resolving the item.

List selection is not happening in swing

HI, I've a JLIST and assigned it a cellRenderer. but i was not able select element in list. Actually it is selected but visually we can not see that it is selected means i was not able to see which item is selected in list.
Screen shot of my list:
and what is expected is
The second screen shot is without CellRenderer. But when i add CellRenderer i was not able to see the selected item in list.
is it normal behaviour that when you add CellRenderer to list.
what am i doing wrong ???
EDIT:-
this is my CellRenderer class:
public class ContactsRender extends JLabel implements ListCellRenderer {
private static final long serialVersionUID = 1L;
ImageIcon img;
public ContactsRender(){
setOpaque(true);
setIconTextGap(12);
setBackground(Color.WHITE);
setForeground(Color.black);
}
#Override
public Component getListCellRendererComponent(JList list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
if(value != null){
User user = (User) value;
String pres = user.getPresence().toLowerCase();
if(pres.contains("unavailable")){
img = new ImageIcon("res/offline.jpg");
} else {
img = new ImageIcon("res/online.jpg");
}
setText(user.getName());
setIcon(img);
return this;
}
return null;
}
You implemented your cell renderer incorrectly. The renderer is responsible for setting the renderer background to the selection color.
Read the JList API and follow the link to the Swing tutorial on "How to Use Lists" where you will find working examples that use a JList. You will also find a section on writing a renderer and an example.
Edit: Also, I just noticed you are reading your icon in the renderer code. You should never do this. The icon should only be read once when the renderer is created and then you cache the Icon. Every time a cell needs to be repainted the renderer is called so it is not efficient to keep reading the icon.
On your cell renderer, you have to implement the case for isSelected is true. For your ListCellRenderer :
Component getListCellRendererComponent(JList<? extends E> list,
E value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
if (!isSelected) doThis(index);
else doThatForSelectedItem(index);
}

Interaction with a cell renderer in JTable

Is there any way to get a cell renderer to respond to mouse events, such as mouseovers?
Never tried it but I guess you would need to:
a) create a custom renderer to paint the cell in two states
b) you need to keep track of which cell should currently be painted in the "mouse over" state
c) add a mouse listener to track mouse entered/exited and mouseMoved. With each event you would need to update a variable that tracks which cell the mouse is positioned over. You
can use the columnAtPoint() and rowAtPoint() methods of JTable
d) when the mouse leaves a cell you need to invoke repaint() on the cell. You can use the getCellRect() method to determine which cell to repaint
e) when the mouse enters a cell you need to reset the cell value for the "mouse over" state and then repaint the cell.
Ok, so I've tried implement camickr's approach, but I've encountered some very nasty problem in the process. I've added a MouseMotionListener to JTable to track the current and previous cell and added some methods that instruct a renderer about which component to return and then repaint the appropriate cell. However, for some odd reason each cell is repainted twice even though there was only one repaint request. Basically, I was able to highlight a cell on a mouse rollover, but it was impossible to remove highlighting from a cell once the mouse cursor leaved it. After initial confusion I've decided to do this in a different way. I've added a method which invokes the editor whe the mouse is over the cell and then added some code that stops editing as soon as the state of JToggleButton (my rendering and editing component) is changed. My current code looks like this :
package guipkg;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
public class Grid extends JTable implements MouseListener {
int currentCellColumn = -1;
int currentCellRow = -1;
int previousCellColumn = -1;
int previousCellRow = -1;
public void detectCellAtCursor (MouseEvent e) {
Point hit = e.getPoint();
int hitColumn = columnAtPoint(hit);
int hitRow = rowAtPoint(hit);
if (currentCellRow != hitRow || currentCellColumn != hitColumn) {
this.editCellAt(hitRow, hitColumn);
currentCellRow = hitRow;
currentCellColumn = hitColumn;
}
}
}
package guipkg;
import javax.swing.table.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TCEditor extends AbstractCellEditor implements TableCellEditor {
/**
* A toggle button which will serve as a cell editing component
*/
JToggleButton togglebutton = new JToggleButton();
public Component getTableCellEditorComponent (JTable Table, Object value, boolean isSelected, int rindex, int cindex) {
/**
* We're adding an action listener here to stop editing as soon as the state of JToggleButton is switched.
* This way data model is updated immediately. Otherwise updating will only occur after we've started to
* edit another cell.
*/
togglebutton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent e) {
stopCellEditing();
}
});
if (value.toString().equals("true")) {
togglebutton.setSelected(true);
}
else {
togglebutton.setSelected(false);
}
togglebutton.setBorderPainted(false);
return togglebutton;
}
public Object getCellEditorValue () {
return togglebutton.isSelected();
}
}
I hope that will help someone

How to add a mouse listener to a JTree so that I can change the cursor (to a hand cursor) when hovering over a node?

As the question states, I'd like to set a mouse listener to my JTree so that I can change the cursor to a HAND_CURSOR when the user places their mouse over a node.
I already have a MouseAdapter registered on my JTree to handle click events, but I can't seem to get a MouseMoved or MouseEntered/MouseExited to work with what I'm trying to do.
Any suggestions?
You need to add a MouseMotionListener/Adapter:
tree.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
int x = (int) e.getPoint().getX();
int y = (int) e.getPoint().getY();
TreePath path = tree.getPathForLocation(x, y);
if (path == null) {
tree.setCursor(Cursor.getDefaultCursor());
} else {
tree.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
}
});
In a JTree, each of tree node is showed by a label generated by the TreeCellRenderer associated to this tree. The usually used class is DefaultTreeCellRenderer which renders this (the DefaultTreeCellRenderer). As a consequence, you can try adding this DefaultTreeCellRenderer a MouseMotionListener to toggle mouse cursor.
Notice adding MouseMotionListener to the tree will simply toggle mouse rendering when on Tree component, not when mouse is on a label.

Categories