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;
}
Related
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.
See below for code sample, the method handleMouseDoubleClick method will take seconds to run and open another layout screen containing buttons and links. End users may click many times on one listed item in the table control and create flood of mouse events, how can I handle the last mouse event only?
Table tableControl = (Table) control;
tableControl.addMouseListener(new MouseAdapter()
{
public void mouseDown(MouseEvent e)
{
handleMouseDown(e);
}
public void mouseUp(MouseEvent e)
{
handleMouseUp(e);
}
public void mouseDoubleClick(MouseEvent e)
{
handleMouseDoubleClick(e);
}
}
Create a flag field. Set it to true when handler was called. Initialize it with false.
You just need to check whether your screen is already initialized or not before creating another one.
Set the cursor to hourglass and/or disable the table, resetting these after the new "layout screen" is closed...
I have this code:
DOM.setEventListener(row.getElement(), new ClickListener(){
#Override
public void onClick(Widget sender) {
// TODO Auto-generated method stub
}});
I think the code is fine and ClickListener extends EventListener, but it gives error saying: The method setEventListener(Element, EventListener) in the type DOM is not applicable for the arguments (Element, new ClickListener(){})
The real answer is that you probably don't. While this is available to attach listeners to events, you may only attach a single listener per element - that listen then gets all dom events that have been configured (see DOM.sinkEvents) - and you are responsible for making sure to detach all listeners before the page unloads, else some browsers will leak memory.
Instead, strongly consider using a Widget (and subclasses) to manage events. RootPanel, the base widget that others should be added to, will manage detaching all other widgets from the page to prevent memory leaks.
Additionally, you are able to listen to the events that happen within there based on the kind of event you are after. For example, even on a widget like a Label that doesnt' normally fire mouseover events, you can still attach handlers and get notification:
Label label = new Label();
label.addDomHandler(new MouseOverHandler() {
#Override
public void onMouseOver(MouseOverEvent event) {
// do something
}
}, MouseOverEvent.getType());
RootPanel.get().add(label);
In most cases, you'll be using existing support methods, like Button to get a click event - there are convinience methods already there for you, thanks to interfaces like HasClickHandlers:
Button button = new Button();
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// do something
}
});
panel.add(button);
More on GWT, Widget, and Memory leaks:
https://developers.google.com/web-toolkit/articles/dom_events_memory_leaks_and_you
http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks
I'm writing an applet and want to figure out how to make a button and a key event cover the same bit of code. For this question, I'll call this button fireButton. The code for the action event would of course look like this:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
//all the code that pressing button executes
}
}
Now, I want pressing the 'enter' key to perform the same code that the action event handles, but I do not want to rewrite all the code again in a keyPressed method.
To be specific, I'm writing a battleship program, and the 'Fire' button takes input from two textFields, handles exceptions, and passes the input as parameters to a method that fires at a particular square in the grid. Ideally, pressing the enter key would function the same way as if I had pressed the fire button. Is there a way to make a certain method call an actionPerformed method? If not, what would be an elegant solution to the problem?
Create an Action
Add the Action to the JButton
Use Key Bindings to bind the Enter key to the Action
Read the Swing tutorial. There are sections on:
How to Use Actions
How to Use Key Bindings
If you are just talking about invoking the "Fire" button with the enter key then check out Enter Key and Button for a couple of approaches.
I suggest you put all the code in a separate method that receives all the relevant data from the event (if any) as parameters:
public void actionPerformed(ActionEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
public void actionPerformed(KeyEvent e) {
if (e.getSource() == fireButton) {
Object relevantData0 = new Object(); // e.getSomething();
Object relevantData1 = new Object(); // e.getSomethingElse();
handleFireAction(relevantData1, relevantData2);
}
}
private void handleFireAction(Object relevantData0, Object relevantData1) { // Object relevantDat2, and so on
//all the code that pressing button executes
}
If you don't need any data from the event its even easier ;)
This way you just write your code once for both events. It's a general OO aproach.
Hope this helps.
Borrowing from MVC I would recommend you have a controller class which handles these sorts of requests. Then all you have to do is delegate to the controller in each event handler.
Like so:
public class BattleShipController {
public void handleFireAction() {
// ...
}
}
//-- in your UI class(es)
private BattleShipController _controller = new BattleShipController();
//-- in event calls:
_controller.handleFireAction();
If you post relevant code I can make further suggestions.
Is there a way to disable the Back button in a browser (basically clearing the History token stack) in GWT? Once I browse to a certain page in my application I want to make sure that the user can't use the back button to go back, but only be able to use links on the page to navigate the site.
You cannot disable a button just intercept it and change its return to something the browser does not understand.
This removes the history:
Window.addWindowClosingHandler(new ClosingHandler() {
#Override
public void onWindowClosing(ClosingEvent event) {
event.setMessage("My program");
}
});
To understand it see: http://groups.google.com/group/google-web-toolkit/browse_thread/thread/8b2a7ddad5a47af8/154ec7934eb6be42?lnk=gst&q=disable+back+button#154ec7934eb6be42
However, I would recommend not doing this because your it goes against good UI practices. Instead you should figure out a way that the back button does not cause a problem with your code.
Call the method below in the onModuleLoad().
private void setupHistory() {
final String initToken = History.getToken();
if (initToken.length() == 0) {
History.newItem("main");
}
// Add history listener
HandlerRegistration historyHandlerRegistration = History.addValueChangeHandler(new ValueChangeHandler() {
#Override
public void onValueChange(ValueChangeEvent event) {
String token = event.getValue();
if (initToken.equals(token)) {
History.newItem(initToken);
}
}
});
// Now that we've setup our listener, fire the initial history state.
History.fireCurrentHistoryState();
Window.addWindowClosingHandler(new ClosingHandler() {
boolean reloading = false;
#Override
public void onWindowClosing(ClosingEvent event) {
if (!reloading) {
String userAgent = Window.Navigator.getUserAgent();
if (userAgent.contains("MSIE")) {
if (!Window.confirm("Do you really want to exit?")) {
reloading = true;
Window.Location.reload(); // For IE
}
}
else {
event.setMessage("My App"); // For other browser
}
}
}
});
}
I found a way to make GWT ignore the back-button: Just add historyitem x if no historyitem was set and do nothing on x.
set a historyitem on startup
History.newItem("x")
in the ValueChangeHandler of History add the following:
String historyToken = event.getValue();
if (!historyToken.equals("x"))
History.newItem("x");
Window.addWindowClosingHandler(new ClosingHandler() {
#Override
public void onWindowClosing(ClosingEvent event) {
event.setMessage("My program");
}
});
That is not a fool proof solution. In fire fox I can press the back button and the onWindowClosing method is never invoked. The reason is that I have used History.newItem() and since history exists the back button or backspace buttons simply navigate through the browser history.
So....fix that :)
Put this in your index.html file:
window.open('html page(For example trial.html)', 'Name of the desired site', width='whatever you want',height='whatever you want', centerscreen=yes, menubar=no,toolbar=no,location=no,
personalbar=no, directories=no,status=no, resizable=yes, dependent=no, titlebar=no,dialog=no');