JAVA javaFX menuBar, multiple items, one method (scene Builder) - java

I have a window built with scene builder with a menuBar.
In the menuBar there are a couple of Menuitems that only open other windows.
So I want to write only one function that can be used by each one of those menuItems, and open the appropriate window.
I tried to give an id for each menuItem, and with this function
public void openWindow(ActionEvent event){
System.out.println( event);
}
I can see that id (example : customer menuItem) ,
javafx.event.ActionEvent[source=MenuItem[id=customers, styleClass=[menu-item]]]
But I dont know how to get it to use it to open the customer window.

In order to get id from ActionEvent you should cast the source of it to MenuItem:
public void openWindow(ActionEvent event){
MenuItem source = (MenuItem) event.getSource();
System.out.println(source.getId());
}
note, that if you are not sure that the source of event is of type MenuItem you can check it like so:
if (event.getSource() instanceof MenuItem) {
MenuItem source = (MenuItem) event.getSource();
System.out.println(source.getId());
}

One option is to get the source of the event (the MenuItem) and retrieve some appropriate data from it (e.g. the id or the userData), as shown in other answers. This will work, but it feels a little fragile as you are relying on string binding and having to perform casts on the types all over the place.
I prefer in this situation just to define a separate method for handling each menu item. Obviously, you can still refactor common functionality into a separate method, in the usual way.
public class MyController {
#FXML
private void openCustomersWindow() {
openWindow("/path/to/customers.fxml");
}
#FXML
private void openOrdersWindow() {
openWindow("/path/to/orders.fxml");
}
// ...
private void openWindow(String resource) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource(resource));
Scene scene = new Scene(loader.load());
Stage newWindow = new Stage();
newWindow.setScene(scene);
newWindow.show();
} catch (Exception exc) {
// handle errors....
}
}
}
and then just use onAction="#openCustomersWindow" for one menu item and onAction="#openOrdersWindow" for another, etc.
Clearly, there is a little repeated code here, but it's not bad (certainly no worse than the amount of repetition in the FXML). If you had enough MenuItems that this were problematic, you would probably want to consider defining them in Java code instead of FXML anyway.

If everything has an ID you can try this.
#FXML void openWindow(ActionEvent event){
try
{
MenuItem tempMenuItem = (MenuItem)event.getSource();
System.out.println(tempMenuItem.getId());
switch(tempMenuItem.getId())
{
case "yourFirstID":
//open your first window here
break;
case "yourSecondID":
//open your second window here
break;
}
}
catch (IOException ex)
{
//catch errors here
}
}

Related

Codename one new gui builder-back command from EVERY form navigation

I am navigating between forms in the NEW GUI builder. The old one had a back button on every form by default.
How do I enable the back button on new gui builder in every form, every time i navigate in a new form? Tried through constants in theme.res. It is still not enabled by default.
Furthermore, is the method "new form1.show" the best way to navigate between forms ? (see code)
Assuming name files:
Main.java, myapplication.java, Form1 ,Form2 ,Form3
Code for navigation, assuming names button1 and Form3:
public void onbutton1ActionEvent(com.codename1.ui.events.ActionEvent ev) {
new Form3().show();
}
Back command from old gui builder, not working here:
public Form showForm(String resourceName, Command sourceCommand) {
try {
Form f = (Form)formNameToClassHashMap.get(resourceName).newInstance();
Form current = Display.getInstance().getCurrent();
if(current != null && isBackCommandEnabled() && allowBackTo(resourceName)) {
f.putClientProperty("previousForm", current);
setBackCommand(f, new Command(getBackCommandText(current.getTitle())) {
public void actionPerformed(ActionEvent evt) {
back(null);
}
});
}
if(sourceCommand != null && current != null && current.getBackCommand() == sourceCommand) {
f.showBack();
} else {
f.show();
}
return f;
} catch(Exception err) {
err.printStackTrace();
throw new RuntimeException("Form not found: " + resourceName);
}
}
I've tried:
form.setBackCommand(cmd);
public Command setBackCommand(String title, ActionListener<ActionEvent> listener)
public void setBackCommand(Command cmd)
public Command setBackCommand(String title, BackCommandPolicy policy, ActionListener<ActionEvent> listener)
public void setBackCommand(Command cmd, BackCommandPolicy policy)
boolean onBack() {
return true;
}
https://www.codenameone.com/blog/toolbar-back-easier-material-icons.html
on main.java and myapplication.java did not accept the commands.
Form3.getToolbar().setBackCommand("", e -> Form3.showBack());
althouth is should not work only for form3, but every form.
Did not work either. Putting "back command" on every sidemenu would not be the ideal solution, because we might be navigating to each form from different forms.
EXTRA:
Is there a way to enable global toolbar and global commands for all forms, so i do not copy paste the toolbar code for each new form? If not answered here, i might make a new thread.
Thanks.
The old GUI builder handled navigation as it was designed at a time when Nokia was the worlds leader in the mobile phone industry and a 4in device was considered large. Back then we assumed the UI was simpler for each form and the navigation was the hard part.
This changed. But the bigger problem for most developers was the concept of stateless navigation which triggered a lot of issues both in design and functionality.
The new GUI builder doesn't include any navigation code or any global code. Each form stands on its own.
Having said that you can implement your own state machine by just keeping form instances and showing the form you want to navigate to e.g.:
public static class Controller {
private static Form1 f1;
private static Form2 f2;
public static void showF1() {
if(f1 == null) f1 = new Form1();
f1.show();
}
// etc...
}
I used static context for simplicity but you can implement your own strategy. Notice that you can also insert global logic here e.g. add the toolbar as a function like:
private static void initForm(Form f) {
// add global commands to the toolbar
}
Alternatively you can derive all the forms from a common base class as the new GUI builder doesn't restrict your inheritance.

How to select a specific node in a TreeViewer?

I built a treeviewer for a specific project, but now I need to select a specific item/node in this treeviewer.
To build the treeviewer, I did this:
viewer = new TreeViewer(composite);
viewer.getTree().setLayoutData(gridData);
viewer.setContentProvider(new FileTreeContentProvider());
viewer.setLabelProvider(new FileTreeLabelProvider());
viewer.setInput(ResourcesPlugin.getWorkspace().getRoot().getProject(folderName.getText()));
viewer.expandAll();
Until here, everything is ok, but now, I don't know how to use listeners to do something when I select a specific item in my tree. Any idea? Thanks.
Edit: I got it!
viewer.addSelectionChangedListener(
new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event) {
if(event.getSelection() instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection)event.getSelection();
Object o = selection.getFirstElement();
if (o instanceof IFile){
IFile file = (IFile)o;
}else {
//what ?
}
}
}
}
);
This is an excellent first step but there is even a better way which is more in the heart and soul of Eclipse.
Your code is listening to local changes but you want to make your code extendable so that other plugins in Eclipse are also notified when someone selects something in your viewer.
Eclipse 4 API
For this to happen you inject ESelectionService into your part and then forward the selection to the workbench by using the listener you already provided.
#Inject
private ESelectionService selectionService;
viewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
// set the selection to the service
selectionService.setSelection(
selection.size() == 1 ? selection.getFirstElement() : selection.toArray());
Then, to catch your own selection:
#Inject
void setSelection(#Optional #Named(IServiceConstants.ACTIVE_SELECTION) IFile pFile) {
if (pFile == null) {
//what ?
} else {
// magic!
}
}
Eclipse 3 API
For this to happen you have to register your viewer with the selection framework. Add this in the createPartControl method of the part where you have added your viewer:
getSite().setSelectionProvider(viewer);
Then, to catch your own selection:
getSite().getPage().addPostSelectionListener(this); // Implement ISelectionListener
References: https://wiki.eclipse.org/E4/EAS/Selection

How to set items of a TableView in a different controller class in JavaFX?

I have two fxml files with two controller classes MainController and PlaylistController. Each class has a TableView, table and nTable respectively.
table is displayed as the default tab on a TabPane
nTable aplies to a new tab created with a button.
What I want is to be able to select items in the default table and add them to a new table on a new tab in a single action (I can do it using two buttons, one of them being clicked after the new tab is created).
I'm guessing I need to make sure the program waits for the new tab to be created before setting the items but I don't know how to do that.
This is the method I'm using:
#FXML
PlaylistController pc;
public static ObservableList<Track> newTracklist = FXCollections.observableArrayList();
public void addToNewPlaylist(){
newTracklist.clear();
newTracklist.addAll(table.getSelectionModel().getSelectedItems());
Tab nTab = new Tab();
FXMLLoader nLoader = new FXMLLoader(getClass().getResource("/fxml/fxPlaylist.fxml"));
try {
Parent nRoot = nLoader.load();
nTab.setContent(nRoot);
} catch (IOException e) {
e.printStackTrace();
}
tabPane.getTabs().add(nTab);
nTab.isClosable();
pc.nTable.setItems(newTracklist);
}
Unless you are defining a constant, or something similar, static fields have no place in a controller.
Define a method in your PlaylistController to take a list of Tracks for display in its table:
public class PlaylistController {
#FXML
private TableView<Track> nTable ;
// ...
public void setTracks(List<Track> tracks) {
nTable.getItems().setAll(tracks);
}
// ...
}
Then just call it when you load the FXML:
public void addToNewPlaylist(){
Tab nTab = new Tab();
FXMLLoader nLoader = new FXMLLoader(getClass().getResource("/fxml/fxPlaylist.fxml"));
try {
Parent nRoot = nLoader.load();
PlaylistController controller = nLoader.getController();
controller.setTracks(table.getSelectionModel().getSelectedItems());
nTab.setContent(nRoot);
} catch (IOException e) {
e.printStackTrace();
}
tabPane.getTabs().add(nTab);
}

How do I get from the "Event" object the object on which there was this event?

I am developing an application on GWT with UIBinder and I have a problem. In my user interface can be a lot of the same elements (eg widgets). All elements must have handlers that will catch the mouse click event. I want to write a universal handler that will be able to identify the widget that caused the event and handle it instead. Now I have a widget for each object to describe the same handler. How can I solve this problem?
You have to cast the source object to the object you expect. The getSource() method only delivers you an object, but you can't access any information from it (e.g. from a Button, you first have to cast it to a button).
Here is an example:
Button bt = new Button("click");
bt.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Object soruce = event.getSource();
if (soruce instanceof Button) { //check that the source is really a button
String buttonText = ((Button) soruce).getText(); //cast the source to a button
RootPanel.get().add(new Label(buttonText));
} else {
RootPanel.get().add(new Label("Not a Button, can't be..."));
}
}
});
RootPanel.get().add(bt);
This of course also works for UiBinder Buttons:
#UiHandler("button")
void onClick(ClickEvent e) {
Object soruce = e.getSource();
if(soruce instanceof Button){
String buttonText = ((Button)soruce).getText();
RootPanel.get().add(new Label(buttonText));
}
else {
RootPanel.get().add(new Label("The event is not bound to a button"));
}
}
If you don't know the type of your element, or the event is bound to more than one element, you have to check for all possible types first, and then perform the right action.
If you want one method to handle click events for a bunch of widgets, then it's as easy as:
#UiHandler({ "firstWidget", "secondWidget", "thirdWidget", "fourthWidget", andSoOn" })
void universalClickHandler(ClickEvent event) {
// here, you can use event.getSource() to get a reference to the actual widget
// that the event targeted.
}
If you want to use event delegation over a bunch of elements, then you need to listen to click events on an ancestor, and then you can use event.getNativeEvent().getEventTarget() to get the actual element that was the target of the click. You can then use isOrHasChild on an Element to know whether the actual target of the click was within that element (e.g. firstElement.isOrHasChild(Element.as(event.getNativeEvent().getEventtarget())), with firstElement being a #UiField Element –or any subclass or Element, such as DivElement)

How to Subscribe to GUI Events in Other JFrames

What is the best practice for subscribing to events from another JFrame? For example, I have a "settings" form, and when the user presses okay on the settings form, I want the main form to know about this so it can retrieve the settings.
Thanks.
Here is my ideal interface:
public void showSettingsButton_Click() {
frmSettings sForm = new sForm(this._currentSettings);
//sForm.btnOkay.Click = okayButtonClicked; // What to do here?
sForm.setVisible(true);
}
public void okayButtonClicked(frmSettings sForm) {
this._currentSettings = sForm.getSettings();
}
Someone publishes an Event, that something has changed, here the settings. A subscriber that registered for this specifig event, gets notified about it and can do his work, here get the settings. This is called publisher/subscriber.
For this you can use Eventbus or implementing something smaller on your own.
One approach is to have only a single JFrame. All the other 'free floating top level containers' could be modal dialogs. Access the the main GUI will be blocked until the current dialog is dismissed, and the code in the main frame can check the settings of the dialog after it is dismissed.
For anyone interested, here is what I ended up going with. I'm not sure if it's the best way, but it is working for my purposes.
// Method called when the "Show Settings" button is pressed from the main JFrame
private void showSettingsButton_Click() {
// Create new settings form and populate with my settings
frmSettings sForm = new frmSettings(this.mySettings);
// Get the "Save" button and register for its click event...
JButton btnSave = sForm.getSaveButton();
btnSave.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt) {
SaveSettings(sForm);
}
});
// Show the settings form
sForm.setVisible(true);
}
// Method called whenever the save button is clicked on the settings form
private void SaveSettings(frmSettings sForm) {
// Get the new settings and assign them to the local member
Settings newSettings = sForm.getSettings();
this.mySettings = newSettings;
}
And if, like me, you are coming from a .NET perspective, here is the C# version:
private void showSettingsButton_Click(object sender, EventArgs e)
{
frmSettings sForm = new frmSettings(this.mySettings);
sForm.btnSave += new EventHandler(SaveSettings);
sForm.Show();
}
private void SaveSettings(object sender, EventArgs e)
{
frmSettings sForm = (frmSettings)sender; // This isn't the exact cast you need..
Settings newSettings = sForm.Settings;
this.mySettings = newSettings;
}

Categories

Resources