JavaFX setting Button style on Mouse Click - java

I've got a problem with a Java project I'm working on: I'm creating a grid of buttons via code in javafx on a pane. The buttons are all types of a subclass of the javafx Button class that i wrote.
Here's the header of the class:
private final String BASIC_STYLE = "-fx-font: 6 arial;";
private final String CLICKED_STYLE = "-fx-background-color: #0f0";
private int row;
private int col;
private String category;
private boolean selected = false;
Within the constructor i do the follwing:
this.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
toggleSelected();
}
});
Here's the toggleSelected() Method:
public void toggleSelected() {
this.selected = !selected;
this.setStyle(selected ? this.BASIC_STYLE : this.BASIC_STYLE+this.CLICKED_STYLE);
}
It's basically supposed to swap the style everytime you click the button. When i click the button, the button first gets selected by the OS (the border is becoming blue) and only after i click a second time on the exact same button it'll become green (the style that i'm giving it via setStyle).
However, the selected property becomes true on the first click and false on the second click, which means i click once on the button and it gets a blue border and selected = true, if i click on it a second time it becomes green and selected = false and if i click on it a third time it becomes normal again but selected will be true again.
I find it to be really strange that the first click on a button changes the "selected" variable correctly but not the style. Why is this happening and how can i avoid that i've to select the button first before i can click it?

You initialize
selected = false ;
and
setStyle(BASIC_STYLE);
But your event handler enforces the rule
selected == true -> setStyle(BASIC_STYLE);
selected == false -> setStyle(CLICKED_STYLE);
So your initial state is inconsistent with the state your handler enforces.
From the initial state, the first time you click, selected is set to true which causes setStyle(BASIC_STYLE) (which is the value it already has, so nothing changes). From then on, everything will switch as required.
You either need to change the initial state, or switch the logic of the setStyle(...) call in the handler.

public class ButtonEnterAction extends Button {
boolean selected = true;
public ButtonEnterAction(String connect) {
setText(connect);
action();
}
public ButtonEnterAction() {
action();
}
private void action() {
EventHandler<KeyEvent> enterEvent = (KeyEvent event) -> {
if (event.getCode() == KeyCode.ENTER) {
fire();
}
};
addEventFilter(KeyEvent.KEY_PRESSED, enterEvent);
// setOnMouseEntered(new EventHandler<MouseEvent>() {
// #Override
// public void handle(MouseEvent me) {
// SepiaTone st = new SepiaTone();
// setEffect(st);
// }
// });
// setOnMouseExited(new EventHandler<MouseEvent>() {
// #Override
// public void handle(MouseEvent me) {
// setEffect(null);
// }
// });
}
#Override
public void fire() {
super.fire(); //To change body of generated methods, choose Tools | Templates.
if (selected) {
SepiaTone st = new SepiaTone();
setEffect(st);
} else {
setEffect(null);
}
selected = !selected;
}
}
Create the Instant Class in ButtonEnterAction is like.
ButtonEnterAction bea = new ButtonEnterAction("TestButton");
bea.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("hello");
}
});

Related

Remove Datepicker rendering from a TableView cell (JavaFX8)

I have a TableView with 2 columns “Date” (LocalDate) and “FX” (Double). I have enabled the cell editing and following an example I found here (http://physalix.com/javafx8-render-a-datepicker-cell-in-a-tableview/) I have created a custom CellFactory that displays a DatePicker for the cells of column “Date”. This solution though renders the DatePciker immediately, so I changed my code to show the DatePicker only when the user double clicks on any of the (non-empty) Date cells. So far so good…
How do I “go back” and remove the DatePicker rendering from the cell after the user has changed the date or cancelled the input? See the pictures as reference. Pic 1 is the initial state of the list. Pic 2 is after double click. How do I go back to Pic 1 status? Let me know if you need to see my specific code.
Reference pictures
This is the code that checks for the double click and then creates the CellFactory
fxTable.getSelectionModel().setCellSelectionEnabled(true);
fxTable.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
TablePosition pos = fxTable.getSelectionModel().getSelectedCells().get(0);
int col = pos.getColumn();
if (col == 0) {
//The code below creates the DatePicker in the cell using the DatePickerCell class that I created following the example in the code I found
tblDateFX.setCellFactory(new Callback<TableColumn<Map.Entry<LocalDate, Double>, String>, TableCell<Map.Entry<LocalDate, Double>, String>>() {
#Override
public TableCell<Map.Entry<LocalDate, Double>, String> call(TableColumn<Map.Entry<LocalDate, Double>, String> param) {
ObservableMap<LocalDate, Double> items = FXCollections.observableMap(myBasket.getEnrtriesCur(curName));
DatePickerCell datePick = new DatePickerCell(items);
return datePick;
}
});
}
}
}
});
This is the DatePickerCell Class
public class DatePickerCell<S, T> extends TableCell<Map.Entry<LocalDate,Double>, String> {
private DatePicker datePicker;
private ObservableMap<LocalDate,Double> curEntries;
public DatePickerCell(ObservableMap<LocalDate,Double> curEntries) {
super();
this.curEntries = curEntries;
if (datePicker == null) {
createDatePicker();
}
setGraphic(datePicker);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
datePicker.requestFocus();
}
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (null == this.datePicker) {
System.out.println("datePicker is NULL");
}
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
setContentDisplay(ContentDisplay.TEXT_ONLY);
} else {
datePicker.setValue(LocalDate.parse(item,df));
setGraphic(this.datePicker);
setText(item);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
}
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
setGraphic(null);
}
private void createDatePicker() {
this.datePicker = new DatePicker();
datePicker.setEditable(true);
datePicker.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
setGraphic(datePicker);
setText(df.format(datePicker.getValue()));
}
});
datePicker.setOnAction(new EventHandler() {
public void handle(Event t) {
LocalDate date = datePicker.getValue();
int index = getIndex();
commitEdit(df.format(date));
if (null != getCurEntries()) {
System.out.println("Modify value");
}
}
});
setAlignment(Pos.CENTER);
}
Have you tried the function setOnEditCommit to do reverse of your code?
column.setOnEditCommit((TableColumn.CellEditEvent<MyObject, Date> t) -> {
//modify the rendering of you cell to normal
});
After some research I found out that the default rendering of a cell in a TableView is a label. So I tweaked the DatePickerCell class to render a label in the "updateItem" method and render the DatePicker only when the label is clicked (meaning that the user wants to edit the date in the cell).
In terms of "going back" I added a listener for "ESC keypressed" on the DatePicker so when that key is pressed (during the edit) a label is rendered and the edit is therefore cancelled. That works quite well!
I'm still trying to figure out how to do the same when the user tries to cancel the edit by clicking somewhere else on the screen.
--
So here's my stab at the DatePickerEdit class.
This is doing what I need. Renders the cells normally at first, only when the user clicks on the date cell the datepicker is rendered. If the user clicks away from the cell, the cell goes back to its initial rendering (same happens when "ESC" is pressed whilst editing or indeed a new date is picked).
Note that I am passing to the class the Observable list that contains the values shown in the TableView. In this way I can update the value in the list directly in the class. Not sure if this is a good practice or not, this was a "forced solution" though. Originally I used the "setOnEditCommit" method for the TableColumn but after some testing I noticed that this event is not always called after the cell is updated (i.e. the commitEdit() method is called for the cell). Not sure if this is a bug or there's something wrong in my code. For sure it does not always happen. On multiple runs, I would say that 1 out of 3 showed this bugged behaviour.
Here's the code, not sure if it's a "good" code or not. I would appreciate any advice in merit.
public class DatePickerCell<S, T> extends TableCell<FX, String> {
private DatePicker datePicker;
private Label lbl;
private ObservableList<FX> currencies;
public DatePickerCell(ObservableList<FX> list) {
super();
lbl=new Label();
this.currencies=list;
if (datePicker == null) {
createDatePicker();
}
}
#Override
public void updateItem(String item, boolean empty) {
// This section here manages the graphic rendering of each cell
// As I don't want to generate the datepicker graphics immediately I just render a label
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
createLabel(item);
}
}
#Override
public void startEdit() {
super.startEdit();
}
#Override
public void cancelEdit() {
super.cancelEdit();
}
private void createDatePicker() {
this.datePicker = new DatePicker();
datePicker.setEditable(true);
// when the user clicks on the label the DatePicker graphics is generated
lbl.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
datePicker.setValue(LocalDate.parse(lbl.getText(),df));
setGraphic(datePicker);
setText(lbl.getText());
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
datePicker.requestFocus();
}
});
// This listener manages the "lost focus" on the picker
datePicker.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
// This combination of OldValue NewValue is generated whenever there is a click outside the DatePicker "graphic area"
// i.e. the calendar (when open), the text filed, the calendar icon OR when a NEW date is selected in the calendar.
// This last case generates the "OnAction" event as well that is managed below.
if (oldValue && !newValue) {
createLabel(df.format(datePicker.getValue()));
}
}
});
// This is generated when a NEW date is picked
// it simply commits the new date and changes the graphics back to a label
datePicker.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
LocalDate date = datePicker.getValue();
int index=getIndex();
if (date!=null) {
commitEdit(df.format(date));
getCurrencies().get(index).setDate(date);
createLabel(df.format(date));
}
}
});
// added this listener in case the user wants to cancel pressing "ESC"
// when this happens the label graphics is rendered
datePicker.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
LocalDate date = datePicker.getValue();
if (event.getCode()== KeyCode.ESCAPE) {
createLabel(df.format(date));
}
}
});
setAlignment(Pos.CENTER_LEFT);
}
private void createLabel(String item) {
lbl.setMinWidth(getWidth());
setGraphic(lbl);
lbl.setText(item);
}
public ObservableList<FX> getCurrencies() {
return currencies;
}
}

Differentiate between mouse drag and mouse click/released

Scenario. I have a graph on which i can perform panning using right click. This works perfectly. Then I added menus on right click perfectly.
Problem. Now right click menus are shown even on mouse release after drag operation completions.
Is there a way to differentiate mouse release and mouse release after mouse drag in Java Swing or JavaFX?
Mouse events are generated independently of one another.
I assume you panning code works with a combination of mousePressed/mouseMoved.
So you need to add some logic to indicate that you are in "panning mode". So if you have a mousePressed followed by a mouseMoved you set a Boolean variable to indicate "panning" mode.
Then in the mouseReleased code you need to check the variable. If "panning mode" then set "panning mode" off and return. Otherwise you are in "popup mode" so you can display the popup.
Since event.isDragDetect() is always true, so I was not able to differentiate events. I created a java class to store a boolean value. This was needed to modify final object state in inner class and was not possible using Boolean wrapper class. Later I am modifying final objects states based on mouse clicked and mouse dragged. I am also checking if mouse release is after drag or without drag as below:-
private void addRightClickMenu() {
final SubMarineBooleanUtilityClass showMenu = new SubMarineBooleanUtilityClass(true);
final MenuItem graphButton1 = new MenuItem("Save Graph as..");
graphButton1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
saveGraphAs();
}
});
final MenuItem graphButton2 = new MenuItem("Reset Graph");
graphButton2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
resetGraph(controlGraph1.getLineChart(), controlGraph2.getLineChart());
}
});
final ContextMenu menu = new ContextMenu(graphButton1, graphButton2);
//Mouse Drag operation cycle=Mouse click+Mouse dragged+Mouse release
getAnchorPaneGraphView().setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(true);
}
}
});
getAnchorPaneGraphView().setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton()) && showMenu.isValueBoolean()) {
menu.show(getAnchorPaneGraphView(), event.getScreenX(), event.getScreenY());
}
}
});
getAnchorPaneGraphView().setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(false);
}
}
});
}
public class SubMarineBooleanUtilityClass {
boolean valueBoolean=false;
/**
* #return boolean
*/
public boolean isValueBoolean() {
return valueBoolean;
}
/**
* Constructor passing a boolean values
* #param value
*/
public SubMarineBooleanUtilityClass(boolean value){
this.valueBoolean=value;
}
/**set boolean value
* #param value
*/
public void setValueBoolean(boolean valueBoolean) {
this.valueBoolean = valueBoolean;
}
}

Making the "Calling method" wait for the "Return" of data

I have 2 classes, one class is a JFrame (MainUIHolder.java) and the other class is a JDialog (EditValuationsDialog.java). MainUIHolder can call EditValuationsDialog on button click event.
Once EditValuationsDialog is open, user can enter data in its fields and press its "Add" button. OK, here is the issue now. Once the user press the "Add" button, the EditValuationsDialog should inform that to the MainUIHolder.
Below is the code.
MainUIHolder
Action edit = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
JTable table = (JTable)e.getSource();
int rowNum = Integer.valueOf(e.getActionCommand());
Object valueAt = table.getModel().getValueAt(rowNum, 0);
EditValuationsDialog edit = new EditValuationsDialog(null,true);
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(table);
edit.setVisible(true);
//CATCH THE CALL FROM EditValuationsDialog HERE!!!!//
}
};
EditValuationsDialog
//Action Listeners
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
return String / int to MainUIHolder (See where I want to catch it in MainUIHolder)
}
else
{
do nothing
}
}
}
In my code I have indicated from where the call to MainUIHolder should be generated and in what place I must catch that call in MainUIHolder. How can I do this call back work?
You could...
Add a static method to EditValuationsDialog that shows the dialog, evaluates the results and returns the value you are expecting...
public void actionPerformed(ActionEvent e)
{
int result = EditValuationsDialog.showDialog();
}
public class EditValuationsDialog ... {
//...
private int result = -1;
//...
public int getResult() {
return result;
}
//...
public static int showDialog(Component source, int rowNum, Object valueAt) {
EditValuationsDialog edit = null;
Window parent = SwingUtilities.windowFor(source);
if (parent instanceof Frame) {
edit = new EditValuationsDialog((Frame)parent,true);
} else if (parent instanceof Dialog) {
edit = new EditValuationsDialog((Dialog)parent,true);
} else {
edit = new EditValuationsDialog(null,true);
}
edit.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
edit.setTitle("Edit Valuations");
edit.setClientName(portfolioViewClientName.getText());
edit.setPortfolioType(portfolioViewInvestmentTypeCombo.getSelectedItem().toString());
edit.setPortfolioId(id);
edit.setOngoingValuationsId(Integer.parseInt(String.valueOf(valueAt)));
edit.setLocationRelativeTo(source);
edit.setVisible(true);
return edit.getResult();
}
//...
private class AddBtnAction implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
if(someCondition)
{
result = 0;
}
else
{
result = 1;
}
EditValuationsDialog.this.dispose();
}
}
}
Or you could...
Simply evaluate the results of getResult() from the above example directly...
Side note: Because I don't like extending from top level containers like JDialog, I tend to create some of my panels/components with static showDialog methods, thing something along the lines of a login panel for example. It means I could re-use the panel else where, but provides me with the convenience of been able to popup a dialog when I need to. I've also used JOptionPane from time to time to show these panels, but it depends on the complexity of the available actions...
Make the dialog modal (setModal(true)). Then the code after dialog.setVisible(true) is executed after the dialog is closed.
BTW it's better to pass the MainUIHolder JFrame instance as parent of the dialog.
You could add an interface to the EditValuationsDialog something like this:
Interface EditValuationsDialogInterface {
public void onAddClicked(addedVlue);
}
and then add it as such:
edit.setOnAddButtonCallback(new EditValuationsDialogInterface () {
#Override
onAddClicked(addedVlue){
//DO SOMETHING
}
});
in your EditValuationsDialog's add button onclick call add this:
onAddButtonClickedCallback.onAddClicked(retunrValue);
This allows you to have a direct link back to the original calling class.

JPopupMenu on JTable -> Get the cell the menu was created on

I have a situation where I have a popup menu created when a JTable is right clicked on. Standard way of creating the popup menu:
aJTable.setComponentPopupMenu(rightClickMenu);
Now afterwards in the action that gets registered, I am unable to find out which cell was right clicked on to get that popup menu to appear.
rightClickMenuItem.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Work out what cell was right clicked to generate the menu
}
});
Any ideas on how you do this?
Astonishing fact: with a componentPopupMenu installed, a mouseListener never sees the mouseEvent that is the popupTrigger (reason is that showing the componentPopup is handled globally by a AWTEventListener installed by BasicLookAndFeel, and that listener consumes the event).
The only place which sees the mousePosition of that trigger is the getPopupLocation(MouseEvent), so the only reliable way to get hold of it (for doing location dependent config/actions) is #Mad's suggestion to override that method and store the value somewhere for later use.
The snippet below uses a clientProperty as storage location:
final JTable table = new JTable(new AncientSwingTeam()) {
#Override
public Point getPopupLocation(MouseEvent event) {
setPopupTriggerLocation(event);
return super.getPopupLocation(event);
}
protected void setPopupTriggerLocation(MouseEvent event) {
putClientProperty("popupTriggerLocation",
event != null ? event.getPoint() : null);
}
};
JPopupMenu popup = new JPopupMenu();
Action action = new AbstractAction("show trigger location") {
#Override
public void actionPerformed(ActionEvent e) {
JPopupMenu parent = (JPopupMenu) SwingUtilities.getAncestorOfClass(
JPopupMenu.class, (Component) e.getSource());
JTable invoker = (JTable) parent.getInvoker();
Point p = (Point) invoker.getClientProperty("popupTriggerLocation");
String output = p != null ? "row/col: "
+ invoker.rowAtPoint(p) + "/" + invoker.columnAtPoint(p) : null;
System.out.println(output);
}
};
popup.add(action);
popup.add("dummy2");
table.setComponentPopupMenu(popup);
#MadProgrammer's suggestion of getPopupLocation looked promising, but I couldn't work out how to get the information across between the table and the actionEvent...
I got around this by making sure that the row was selected when you rightclicked on it -> since the popup menu prevents the selection of the row, you can add in a mouse listener that makes sure the row gets selected no matter what click (left or right) is pressed.
aTable.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
int r = aTable.rowAtPoint(e.getPoint());
if (r >= 0 && r < clt.getRowCount()) {
aTable.setRowSelectionInterval(r, r);
} else {
aTable.clearSelection();
}
}
});
This means that in the rightClickMenuItem's action listener, you can grab the table's selected cell / row
rightClickMenuItem.addActionListener(new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
aTable.get details about the selected one....
}
});
Too easy! Thanks everyone for the help.
JTable has methods
int row = rowAtPoint(p);
int col = columnAtPoint(p);
So pass the MouseEvent's point and use the values
Add a MouseListener and store the last right click point somewhere.

How to keep the popup menu of a JComboBox open on populating it?

I have a JComboBox on my Panel. One of the popup menu items is 'More' and when I click that I fetch more menu items and add them to the existing list. After this, I wish to keep the popup menu open so that the user realizes that more items have been fetched however, the popup closes. The event handler code I am using is as follows
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == myCombo) {
JComboBox selectedBox = (JComboBox) e.getSource();
String item = (String) selectedBox.getSelectedItem();
if (item.toLowerCase().equals("more")) {
fetchItems(selectedBox);
}
selectedBox.showPopup();
selectedBox.setPopupVisible(true);
}
}
private void fetchItems(JComboBox box)
{
box.removeAllItems();
/* code to fetch items and store them in the Set<String> items */
for (String s : items) {
box.addItem(s);
}
}
I do not understand why the showPopup() and setPopupVisible() methods are not functioning as expected.
add the following line in the fetchItems method
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
box.showPopup();
}
}
If u call selectedBox.showPopup(); inside invokelater also it will work.
overwrite the JCombobox setPopupVisible metod
public void setPopupVisible(boolean v) {
if(v)
super.setPopupVisible(v);
}
jComboBox1 = new javax.swing.JComboBox(){
#Override
public void setPopupVisible(boolean v) {
super.setPopupVisible(true); //To change body of generated methods, choose Tools | Templates.
}
};
I found some simple solution to always keep popup open. It may be useful with some custom JComboBox'es, like the one I have in my project, but is a little hacky.
public class MyComboBox extends JComboBox
{
boolean keep_open_flag = false; //when that flag ==true, popup will stay open
public MyComboBox(){
keep_open_flag = true; //set that flag where you need
setRenderer(new MyComboBoxRenderer()); //our spesial render
}
class MyComboBoxRenderer extends BasicComboBoxRenderer {
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
if (index == -1){ //if popup hidden
if (keep_open_flag) showPopup(); //show it again
}
}
}
}

Categories

Resources