I am trying to setup a button similar to the save button with the default CRUD database template (where the button only becomes active if a variable is true). I have looked at the code for the save button and worked out that i need:
A variable to link it with (saveNeeded in their case)
An action to run
I have recreated both of these on another button but it never seams to get enabled. I have print statements on 2 other buttons i am using to set the variable i have my button linked to to true and false so i can see the value is changing.
Is there some crucial step i am missing? this seems like it should be fairly straight forward.
One other thing, if i manualy change the variable to true in my constructor to true and run the application it enables the button and false disables it so that part is working, just not the change.
Any help would be appreciated as i have spent the last few hours trying and can not figure it out
Thanks
The variable or "property" needs to be watched somehow, perhaps by using a PropertyChangeSupport object and allowing other objects to add a PropertyChangeListener to it, making it a "bound property". There's a special version of this for Swing applications that takes care with the Swing event thread, SwingPropertyChangeSupport, and you may wish to use it.
Edit
You asked
Thanks for the reply, i assume that would be what firePropertyChange("saveNeeded", !saveNeeded, saveNeeded); is doing but waht is this doing? does this just notify the program or do i need to catch an handle this somewhere. This is based off the pre generated code so im not sure if it added something in the background.
The class that holds the watched variable would need a private SwingPropertyChangeSupport field. You would give it a public addPropertyChangeListener method where you'd allow other classes to listen to its bound properties, something like this (if the property were a String):
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Foo {
public static final String MY_BOUND_PROPERTY = "My Bound Property";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
private String myBoundProperty;
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
public String getMyBoundProperty() {
return myBoundProperty;
}
public void setMyBoundProperty(String myBoundProperty) {
Object oldValue = this.myBoundProperty;
Object newValue = myBoundProperty;
this.myBoundProperty = myBoundProperty;
PropertyChangeEvent pcEvent = new PropertyChangeEvent(this,
MY_BOUND_PROPERTY, oldValue, newValue);
spcSupport.firePropertyChange(pcEvent);
}
}
Then any class that would like to listen for changes would simply add a PropertyChangeListener to an object of this class and respond to changes as it saw fit.
Related
I have many .java files within my project. From FTall.java i want to access {text field} t1 ('main' jFrame -> jPanel2) of the FormTTS.java
I am right now getting errors due to that only, because it cannot find symbol t1.
It is private and i cant change it to public
Edit:
I am using this code already to open up FTall from the FormTTS.java:
In a button in FormTTS
FTall forma = new FTall();
JFrame frame = forma.getFrame();
forma.setVisible(true);
and this in FTall
public JFrame getFrame() {
return jFrame1;
}
Because of the way your code is structure, you need to supply some way for FormTTS.t1
In FormTTS, provide a method to exposes t1, something like getMainTextField for example...
public JTextField getMainTextField() {
return t1;
}
You're next problem is FTall is going to need a reference to an instance of FormTTS. Probably the easiest way would be to pass a reference to the constructor of FTall
private FormTTS mainForm;
public FTall(FormTTS mainForm) {
this.mainForm= mainForm;
}
This will allow you to access t1 by simply using the mainForm reference...
JTextField field = mainForm.getMainTextField();
Personally, I would prefer not to expose the text field as it gives too much access to callers, instead I'd prefer to return the text and if required provide a means to change it...
So in FormTTS, I might do something like...
public String getMainText() {
return t1.getText();
}
// Do this only if you need to have write access
public void setMainText(String text) {
t1.setText(text);
}
But that's just me...
To obtain the value, you would use a similar approach as above (to getting the text field)
String text = mainForm.getMainText();
if i am understanding your question its simple first ensure that your text field come in to scope before access and once it come in, then use a setter to set its refrence in required class then you can access it.
I am trying to get a TextArea to autoscroll to the bottom with new text which is put in via an event handler. Each new entry is just one long string of text with each entry separated by a line break. I have tried a change handler which sets setscrolltop to Double.MIN_VALUE but to no avail. Any ideas of how this could be done?
You have to add a listener to the TextArea element to scroll to the bottom when it's value is changed:
#FXML private TextArea txa;
...
txa.textProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observable, Object oldValue,
Object newValue) {
txa.setScrollTop(Double.MAX_VALUE); //this will scroll to the bottom
//use Double.MIN_VALUE to scroll to the top
}
});
But this listener is not triggered when you use the setText(text) method, so if you want to trigger it after a setText(text) use the appendText(text) right after it:
txa.setText("Text into the textArea"); //does not trigger the listener
txa.appendText(""); //this will trigger the listener and will scroll the
//TextArea to the bottom
This sounds more like a bug, once the setText() should trigger the changed listener, however it doesn't. This is the workaround I use myself and hope it helps you.
txa.appendText("") will scroll to the bottom without a listener. This becomes an issue if you want to scroll back and the text is being constantly updated. txa.setText("") puts the scroll bar back at the top and same issue applies.
My solution was to extend the TextArea class, ammend the FXML tag from textArea to LogTextArea. Where this works, it clearly causes problems in scene builder as it does not know what this component is
import javafx.scene.control.TextArea;
import javafx.scene.text.Font;
public class LogTextArea extends TextArea {
private boolean pausedScroll = false;
private double scrollPosition = 0;
public LogTextArea() {
super();
}
public void setMessage(String data) {
if (pausedScroll) {
scrollPosition = this.getScrollTop();
this.setText(data);
this.setScrollTop(scrollPosition);
} else {
this.setText(data);
this.setScrollTop(Double.MAX_VALUE);
}
}
public void pauseScroll(Boolean pause) {
pausedScroll = pause;
}
}
I don't have enough reputation to comment, but wanted to give some insight for future readers as to why setText doesn't appear to trigger the listener, but appendText does, as in Math's answer.
I Just found this answer while encountering similar issues myself, and looked into the code. This is currently the top result for 'javafx textarea settext scroll' in a google search.
setText does indeed trigger the listener.
According to the javadoc on the doSet method in TextInputControl (TextArea's superclass):
* doSet is called whenever the setText() method was called directly
* on the TextInputControl, or when the text property was bound,
* unbound, or reacted to a binding invalidation. It is *not* called
* when modifications to the content happened indirectly, such as
* through the replaceText / replaceSelection methods.
Inside the doSet method, a call is made to updateText(), which TextArea overrides:
#Override final void textUpdated() {
setScrollTop(0);
setScrollLeft(0);
}
So, when you set the scroll amount in the listener as in Math's answer, the following happens:
The TextProperty is updated
Your listener is called, and the scroll is set
doSet is called
textUpdated is called
The scroll is set back to the top-left
When you then append "",
The TextProperty is updated
Your listener is called, and the scroll is set
The javadoc is above is clear why this is the case - doSet is only called when using setText.
In fact, appendText calls insertText which calls replaceText - and the javadoc further states that replaceText does NOT trigger a call to doSet.
The behaviour is rather irritating, especially since these are all final methods, and not obvious at first glance - but is not a bug.
Alternative to that strange setText bug without using appendText
textArea.selectPositionCaret(textArea.getLength());
textArea.deselect(); //removes the highlighting
One addendum I would add to jamesarbrown's response would be to this would be to use a boolean property instead so you can access it from within FXML.
Something like this.
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TextArea;
public class LogTextArea extends TextArea {
private final BooleanProperty pausedScrollProperty = new SimpleBooleanProperty(false);
private double scrollPosition = 0;
public LogTextArea() {
super();
}
public void setMessage(String data) {
if (isPausedScroll()) {
scrollPosition = this.getScrollTop();
this.setText(data);
this.setScrollTop(scrollPosition);
} else {
this.setText(data);
this.setScrollTop(Double.MAX_VALUE);
}
}
public final BooleanProperty pausedScrollProperty() { return pausedScrollProperty; }
public final boolean isPausedScroll() { return pausedScrollProperty.getValue(); }
public final void setPausedScroll(boolean value) { pausedScrollProperty.setValue(value); }
}
However, the problem with this answer is that if you get flooded with an unreasonably large amount of input (as can happen when retrieving a log from an IO Stream) the javaFX thread will lock up because the TextArea gets too much data.
As Matthew has posted the setText call is the problem. A easy workaround is to call clear, appendText and then setScrollTop. The other suggestions above did not work well for me, with enough delay it worked but was unreliable behaviour.
textAreaListener = (observable, oldValue, newValue) -> {
textArea.clear();
textArea.appendText(newValue);
textArea.setScrollTop(Double.MAX_VALUE);
};
I have a textbox and one suggestbox. I attach a value change and key up handler to the text box such that whatever the user types (or pastes) into the text box is echo-ed inside the suggestbox. I can get the suggestbox to display the suggestion list by calling showSuggestionList on each value change and key up event.
Now, how do I get the suggestbox to automatically choose the first item in the suggestion list?
One of the methods I tried is to programatically simulate key presses, i.e
suggestBox.setFocus(true);
NativeEvent enterEvent = Document.get().createKeyPressEvent(false, false, false, false, KeyCodes.KEY_ENTER);
DomEvent.fireNativeEvent(enterEvent, suggestBox);
textBox.setFocus(true);
This doesn't work at all. The enter key isn't simulated. Another possible solution is to extend SuggestionBox.SuggestionDisplay, but I'm not too sure how to that. Any pointers appreciated.
Update: I'm still working on this and trying various methods.
Here, I tried to implement my own SuggestionDisplay by subclassing DefaultSuggestionDisplay and overriding getCurrentSelection() to make accessible from my class. This doesn't work either. Null is returned.
private class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
#Override
protected Suggestion getCurrentSelection() {
return super.getCurrentSelection();
}
}
suggestBox.setAutoSelectEnabled(true);
textBox.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event) {
suggestBox.setValue(textBox.getText(), true);
suggestBox.showSuggestionList();
if (suggestBox.isSuggestionListShowing()) {
String s = ((CustomSuggestionDisplay) suggestBox.getSuggestionDisplay()).getCurrentSelection().getDisplayString();
Window.alert(s);
}
}
});
Here, I tried to attach a value change handler to the SuggestBox, and casting the event type to SuggestOracle.Suggestion. Again, null is returned.
suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String s = ((SuggestOracle.Suggestion) event).getDisplayString();
Window.alert(s);
}
});
Use suggesBox.setAutoSelectEnabled(true)
Here more info about the SuggestBox of GWT:
You could try using addSelectionHandler in conjunction with setAutoSelectEnabled to receive an event whenever a suggestion is selected. You could also have your Oracle send a message when it suggests something, or your Display send a message when it displays a list:
public class AutomaticallySelectingSuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
#Override
protected void showSuggestions(SuggestBox box, Collection<? extends SuggestOracle.Suggestion> suggestions, boolean isDisplayHtml, boolean isAutoSelectEnabled, SuggestBox.SuggestionCallback callback) {
super.showSuggestions(box, suggestions, isDisplayHtml, isAutoSelectEnabled, callback);
fireValueChangeEventWithFirstSuggestion(suggestions);
}
}
This idea feels a little muddled to me, so I hope you can find a solution just using event handlers.
I am trying to use the MVC design.
In the model I wanted a method like this
public boolean changeSomeData(...){
boolean b;
//create a dialog with an OK button
return b;
}
I want the method to return TRUE if the changes were actually made. The changes are done inside the actionPerformed method of the OK button.
My problem is that I can't write b=true; inside the actionPerform of the OK button, because I have to declare b as final in order to use it in the actionPerformed().
What I did is creating a class
private class MyBoolean {
boolean b;
}
and then
public boolean changeSomeData(...){
MyBoolean myBoolean;
//create a dialog with an OK button
actionPerformed(){
//make changes in the data
myBoolean.b=true;
}
boolean b = myBoolean.b;
return b;
}
But I don't feel good about this solution and I wanted to know if it is correct what I did and if there is a better solution.
Should I better throw an exception if the changes aren't made? (for example, if the user clicks "cancel" instead of "ok")
In the model I wanted a method like this ... //create a dialog with an OK button
I'd say this is a flaw already, since the model should not do anything with views directly.
A better approach would be to open the dialog (using the controller), register the controller for the ActionEvent of "OK" (and thus actionPerformed) and then do whatever changes should be done in that method.
Edit:
You might want to consider the following rough approach:
The views register themselves or associated classes to the model as listeners. Whenever the model is changed it fires events to notify the views of the change.
The controller registers itself on the views and is notified when the views change. If a user changes data, the controller then might open the dialog and only commit the changes of the user signals "OK". Thus the model has never to check itself if data needs to be changed. That is actually the controller's task and if the controller passes changes to the model, it should apply them.
A better way to achieve your task is to keep a variable on the dialog that indicates if a successful change was made. Then have a method that your model class calls to retrieve the value and return it.
Something like:
public boolean changeSomeData(...){
//create a dialog with an OK button
return dialog.isSuccess();
}
One way you could make this code a bit cleaner...
public boolean changeSomeData() {
// note that this is not a class boolean, no need to do extra autoboxing.
boolean dataChanged = false;
// check the old value against the new value
// for classes
if (oldvalue.equals(newValue)) {
oldValue = newValue;
dataChanged = true;
}
// for pimitives (built-ins)
if (oldvalue == newValue) {
oldValue = newValue;
dataChanged = true;
}
// odds are good that the above action performed was supposed to call this
// changeSomeData() and not the other way around.
// if you must fire actionPerformed() when data has changed, then do so
// like this, otherwise if it was added as part of the "solution" you can
// skip it.
if (dataChanged) {
actionPeformed();
}
return dataChanged;
}
Note that this code is Controller code, as it manipulates the model directly, and (possibly) updates views.
I've problem regarding GUI with one Menu and one Order Class.
I've created a variable to store how many items have been selected in the Menu Class.
private int totalSelected;
The var totalSelected is live updated. It can be changed anytime depending on actionPerformed() function.(Exp: totalSelected will add up all the selected items)
In the Order Class, how can I access to the live update variable totalSelected in order to retrieve the live update value? When I invoke getTotalSelected() function inside the Menu Class, I will only obtain a 0 value.
Thanks for your help ^^!
Please allow me to specify my question clearer.
public class MenuTab extends JPanel
{
private JLabel display;
private int totalSelected;
public MenuTab()
{
....
}
}
public getTotalSelected(){
return totalSelected;
}
private class SelectedListener implements ActionListener
{
public void actionPerformed()
{
.......
//Assume that totalSelected has been updated!
display = new JLabel("Total: " + totalSelected);
// OK to display totalSelected live value here.
}
}
// A new class is the confirmation of order
public class OrderConfirmedTab extends JPanel{
private JLabel displayTotal;
private MenuTab order = new MenuTab();
public OrderConfirmedTab()
{
......
int totalSelected = order.getTotalSelected();
displayTotal = new JLabel("Total: " + totalSelected);
// Problem to display totalSelected live value here.
// Will obtain 0;
// How can I obtain the live updated value from class MenuTab? Thanks!
}
}
If I read your code right, you need to make your variable be private static int totalSelected; You need to make it static so that it stays the same for all instances of the class.
I looks like your not updating the private int totalSelected; variable when a user makes a selection, so it is always 0.
Ya! I just realized that my JLabel
will not update the value
automatically. So how can I fix it?
Thanks! – Christine
If I understand you correctly you have two GUIs where changes in one (the MenuTab) will update the other (OrderConfirmedTab) in real time?
If so, you will need to increase the coupling between the two objects. If MenuTab has a reference back to OrderConfirmedTab then it can call methods to update the value as it changes.
For example, pass OrderConfirmedTab into MenuTabs constructor
MenuTab mt = new MenuTab(this); // from within OrderConfirmTabs costructor
Then when MenuTab has an actionPerformed event it can call back to OrderConfirmTab
orderConfirmTab.setTotalSelected(totalSelected); // you have to create this method in OrderConfirmTab
I hope this helps a little
You can use PropertyChangeListener and PropertyChangeSupport mechanisms to dispatch an event when the value is updated and to be notified when the variable has changed. Your JLabel is not going to update on its own; even if you were to use an object other than a primitive (note that primitives are merely values, while objects are actually implicit pointers); you will need to update your JLabel when the variable changes, since the JLabel simply stores a string, not a reference to the variables from which the string was constructed.
This is the concept of model-view-controller; your variable should be in some sort of class or classes that represent the model (the information) and which allow changes to be observed via property change events. Your view classes should simply provide display logic and no business or application-specific logic. It is the controller in which your application logic should reside; your controller should register for events on the model, and it should update the view whenever the model has changed, and it should likewise update the model when the view dispatches events that should result in the model being changed.