JavaFX ChoiceBox ContextMenu shown again when it is displayed and clicked again - swing

I have JavaFX panel with ChoiceBox in Swing application. Standard behaviour of ChoiceBox is that when you click it for the first time the popup menu with items is shown and when you click ChoiceBox for the second time, the popup menu is hidden. But when you put it to Swing application the second click causes popup to hide and to be shown immediately again. How can I prevent this behaviour?
public class ComboTest {
private static void initAndShowGUI() {
JFrame frame = new JFrame("FX");
final JFXPanel fxPanel = new JFXPanel();
fxPanel.setPreferredSize(new Dimension(100, 100));
frame.add(fxPanel);
frame.pack();
frame.setVisible(true);
Platform.runLater(new Runnable() {
#Override
public void run() {
initFX(fxPanel);
}
});
}
private static void initFX(JFXPanel fxPanel) {
// This method is invoked on JavaFX thread
Scene scene = createScene();
fxPanel.setScene(scene);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
initAndShowGUI();
}
});
}
private static Scene createScene() {
ChoiceBox choiceBox = new ChoiceBox(FXCollections.observableArrayList("item 1", "item 2"));
VBox vbox = new VBox(choiceBox);
return new Scene(vbox);
}
}
My suspicion is that when I click the choicebox for the second time the popup loses focus which causes it to hide and the choicebox then handles mouse click and shows the popup again.

I believe that this problem caused by the existing ChoiceBox bug in javafx.
The simplest fix is just to use ComboBox instead:
ComboBox<String> choiceBox = new ComboBox<>(FXCollections.observableArrayList("item 1", "item 2"));

Related

Vaadin Drag Drop Component

We are creating a web application using Vaadin. Our application contains alot of drag and drop features.
We have an object which is drag-able.
We can click on it to open its menu as well.
Sometimes that when we click that item it behaves as if it is dragged.
When this happens we are unable to open its menu because the component is in dragmode.
All components with the same functionality behave the same however in development environment, when we restart the tomcat the problem disappeared?
I noticed that when the components start showing me this behavior the webpage in FireFox the behavior is fine there?
A simple solution to this could be to introduce a drag mode/edit button which would allow the user to switch the drag mode on and off.
This would mean the user could interact with the components and then enter this "drag mode" when they wished to drag them. Hence reducing the frustration of trying to interact with the component and it starting to "drag" instead.
I've create a simple example program to try out below.
public class DemoUI extends UI {
HorizontalSplitPanel splitPanel;
DragAndDropWrapper wrapperA;
DragAndDropWrapper wrapperB;
DragAndDropWrapper splitPaneWrapper;
Button buttonA;
Button buttonB;
private boolean isDragMode = false;
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = DemoUI.class)
public static class Servlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
final HorizontalSplitPanel splitPanel = new HorizontalSplitPanel();
Button buttonA = new Button("Button A");
Button buttonB = new Button("Button B");
final DragAndDropWrapper wrapperA = new DragAndDropWrapper(buttonA);
final DragAndDropWrapper wrapperB = new DragAndDropWrapper(buttonB);
final VerticalLayout leftPanel = new VerticalLayout();
final VerticalLayout rightPanel = new VerticalLayout();
DragAndDropWrapper leftPanelWrapper = new DragAndDropWrapper(leftPanel);
DragAndDropWrapper rightPanelWrapper = new DragAndDropWrapper(rightPanel);
buttonA.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Button A was clicked");
}
});
buttonB.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Notification.show("Button B was clicked");
}
});
leftPanelWrapper.setDropHandler(new DropHandler() {
#Override
public void drop(DragAndDropEvent event) {
leftPanel.addComponent(event.getTransferable().getSourceComponent());
}
#Override
public AcceptCriterion getAcceptCriterion() {
return AcceptAll.get();
}
});
rightPanelWrapper.setDropHandler(new DropHandler() {
#Override
public void drop(DragAndDropEvent event) {
rightPanel.addComponent(event.getTransferable().getSourceComponent());
}
#Override
public AcceptCriterion getAcceptCriterion() {
return AcceptAll.get();
}
});
final Button dragMode = new Button("Drag Mode On");
dragMode.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
isDragMode = !isDragMode;
if (isDragMode) {
dragMode.setCaption("Drag Mode Off");
wrapperA.setDragStartMode(DragStartMode.WRAPPER);
wrapperB.setDragStartMode(DragStartMode.WRAPPER);
} else {
dragMode.setCaption("Drag Mode On");
wrapperA.setDragStartMode(DragStartMode.NONE);
wrapperB.setDragStartMode(DragStartMode.NONE);
}
}
});
leftPanel.setSizeFull();
rightPanel.setSizeFull();
leftPanelWrapper.setSizeFull();
rightPanelWrapper.setSizeFull();
leftPanel.addComponent(wrapperA);
rightPanel.addComponent(wrapperB);
splitPanel.setFirstComponent(leftPanelWrapper);
splitPanel.setSecondComponent(rightPanelWrapper);
splitPanel.setSizeFull();
VerticalLayout layout = new VerticalLayout();
layout.addComponent(dragMode);
layout.addComponent(splitPanel);
layout.setSizeFull();
this.setContent(layout);
this.setSizeFull();
}
.
All the best.

Swing UI Delay adding and removing elements

I have a form stored in both the inputWidget and the outputWidget. The buttons addInput and addOutput will show two different forms in the secondaryInOutPanel.
However there is a significant delay when moving between the form by clicking the buttons. In fact it changes when I attempting to click on the form. And there is still some visible drawings from the pervious form.
I tried using SwingUtilities but that caused the delay to be worst.
secondaryInOutPanel = new JPanel(new BorderLayout());
secondaryInOutPanel.setMinimumSize(new Dimension(200,400));
JPanel btnPanel = new JPanel();
outinPanel.add(btnPanel, BorderLayout.NORTH);
JButton addInput = new JButton("Add Input");
btnPanel.add(addInput);
addInput.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
secondaryInOutPanel.removeAll();
secondaryInOutPanel.add(inputWidget, BorderLayout.NORTH);
JButton addBtn = new JButton("Save Input");
secondaryInOutPanel.add(addBtn, BorderLayout.SOUTH);
addBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
});
}
});
JButton addOutput = new JButton("Add Output");
btnPanel.add(addOutput);
addOutput.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
secondaryInOutPanel.removeAll();
secondaryInOutPanel.add(outputWidget, BorderLayout.NORTH);
JButton addBtn = new JButton("Save Output");
secondaryInOutPanel.add(addBtn, BorderLayout.SOUTH);
addBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
}
});
}
});
A better design is to use a Card Layout to hold your input and output panels. Then you can just swap panels as required. The CardLayout will then manage the revalidating and repainting of the panel for you.
You need to make a call to revalidate() and or repaint() on the secondaryInOutPanel after you make changes.

Draw custom stuff on top of opaque components in a JPanel

I have an JPanel populated with several opaque custom components. Now I would like to draw something on top of these components by overriding the paintComponent() method. My problem is that the painted stuff is placed behind the embedded components and, as they are opaque, is covered by them.
Is there any way to let the painting appear on top of the components?
Here's a short example of what I'm trying to do:
public class DrawOnTop {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame("Draw on top");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
});
}
}
class MyPanel extends JPanel {
public MyPanel() {
setLayout(new BorderLayout(3, 3));
add(new JButton("Button 1"), BorderLayout.NORTH);
add(new JButton("Button 2"), BorderLayout.CENTER);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.red);
g.drawLine(0, 0, getVisibleRect().width, getVisibleRect().height);
}
}
You were thinking along the right lines.
Only problem was that you should have Overridden the paintChildren() method like in the code below. This is because the paintComponent() method is called as first and does the background etc painting of the component itself (the MyPanel), then is called paintBorders() and lastly the paintChildren() which paints all that is inside of the component calling it.
public class DrawOnTop {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Draw on top");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
});
}
}
class MyPanel extends JPanel {
public MyPanel() {
setLayout(new BorderLayout(3, 3));
JButton b1 = new JButton("Button 1");
MouseListener ml = new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
super.mouseExited(e);
MyPanel.this.repaint();
}
};
b1.addMouseListener(ml);
JButton b2 = new JButton("Button 2");
b2.addMouseListener(ml);
add(b1, BorderLayout.NORTH);
add(b2, BorderLayout.CENTER);
}
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
g.setColor(Color.red);
g.drawLine(0, 0, getVisibleRect().width, getVisibleRect().height);
}
}
Important to notice, in the code sample, that I added also a MouseListener to repaint the panel when a mouse exits a button, otherwise the buttons would always stay over the line once mouse enters over one of them.
But if you want to have a custom painting that is always on top of your components then I would suggest to use a glass pane. Examples of a glass pane use are provided here:
Simple one.
A more complex one.

How to synchronize popups visibility of two JComboBoxes (comboBox and mirrorComboBox)

I am trying to synchronize popups of two combo boxes - call them comboBox and mirrorComboBox.
I want to set popup of mirrorComboBox visible when popup of comboBox becomes visible.
I tried to implement this behavior by adding PopupMenuListener to comboBox and calling mirrorComboBox.setPopupVisible(true) when popupMenuWillBecomeVisible event occurs. It works fine, but unfortunatelly it causes another problem - the popup of comboBox will never be hidden! Event the popupMenuWillBecomeInvisible method is never called after the popup is once set visible.
How to sync popups visibility of two combo boxes?
Here is my implementation:
import java.awt.FlowLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
public class MirrorPopupsMainFrame extends JFrame implements PopupMenuListener {
public static void main(String[] args) {
new MirrorPopupsMainFrame().setVisible(true);
}
JComboBox comboBox;
JComboBox mirrorComboBox;
public MirrorPopupsMainFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buildUi();
}
protected void buildUi() {
String[] items = new String[]{"item1", "item2", "item3"};
comboBox = new JComboBox(items);
comboBox.addPopupMenuListener(this);
mirrorComboBox = new JComboBox(items);
setLayout(new FlowLayout());
add(comboBox);
add(mirrorComboBox);
setBounds(0, 0, 300, 200);
}
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// Not calling the following line will cause
// comboBox's popup to hide correctly.
mirrorComboBox.setPopupVisible(true);
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
mirrorComboBox.setPopupVisible(false);
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {}
}

Creating glasspane

I have a question. Can I create glasspane in body MousePressed ? If yes anyone can write me how? I mean that I press mouse button and glass pane is visible and I can painting on him.
EDIT
Ok I have now what I want. My glass pane is creating when I click mouse button and disapear when I release this button. Now I have another question. Where I should create my painting method. I want draw rectangle on this glasss pane using mouse dragged. Where I must implement paint method? In other class or in this events? I implement one my try paint function but I don't know if this is good way. This is my code:
public class Selection extends JPanel
{
static Point startPoint;
public static void GUI()
{
final JFrame frame = new JFrame();
JPanel panel = new JPanel();
JButton button = new JButton("Select");
final JPanel glassPane = new JPanel();
frame.setSize(400, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.add(button);
glassPane.setOpaque(false);
frame.add(panel);
frame.setGlassPane(glassPane);
glassPane.addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
System.out.println("f.getGlassPane() mousePressed");
if(e.getButton() == MouseEvent.BUTTON1)
frame.getGlassPane().setVisible(true);
startPoint=e.getPoint();
Graphics2D g = null;
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double();
rect.setFrameFromDiagonal(e.getPoint().x, e.getPoint().y,startPoint.x, startPoint.y);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5F));
g2.setColor(Color.BLUE);
g2.fill(rect);
g2.draw(rect);
}
});
glassPane.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e)
{
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
});
frame.addMouseListener(new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
super.mousePressed(e);
if(e.getButton() == MouseEvent.BUTTON1)
frame.getGlassPane().setVisible(true);
}
public void mouseReleased(MouseEvent e)
{
frame.getGlassPane().setVisible(false);
}
});
frame.setVisible(true);
}
int x1, x2, y1,y2;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawRect(x1,y1, x2, y2);
}
public static void main(String[] args)
{
GUI();
}
}
Hi please check out my answer to some other question where I present a way in which a glass pane can be used to simulated dialog behaviour. There you have shown how to show it and hide it on mouse click in my case right mouse click. This example should get you started nicely.
I see no problem creating a glasspane and attaching it to a RootPaneContainer from inside moussePressed() method.
However, I may wonder why create a new glass pane every time the user clicks the mouse; that wouldn't be very performant; it is probably wiser to create and attach a glass pane up front and then change its content during mouse click).
Now, regarding "painting on the glass pane", it depends on what you mean by "painting", if this means using a "Graphics" instance to directly draw on the glass pane, the answer is NO (well, actually you could but, your painting would disappear at first UI refresh...)
Such painting must occur in paintComponent() method of your glass pane (that you must override).