I have played for a while with History in gwt because i intend to implement it in my current project, so i created a small demo project just to see how it works and do some practice.
So, for some reasons it does not work.
Here is the code - very simple back and forward.
here is the iframe
<iframe id="__gwt_historyFrame" style="width:0;height:0;border:0"></iframe>
then the entry point
public class EntryPoint implements com.google.gwt.core.client.EntryPoint {
private FirstPanel panel;
public void onModuleLoad() {
ContentPanel panel = ContentPanel.getInstance();
panel.setContent(FirstPanel.getInstance());
RootPanel.get().add(panel);
}
}
and 3 singleton forms, content where form are being loaded and 2 form.
public class ContentPanel extends Composite
{
private static ContentPanel instance;
private static VerticalPanel panel = new VerticalPanel();
private ContentPanel()
{
initWidget(panel);
}
public void setContent(Widget widget)
{
panel.clear();
panel.add(widget);
}
public static ContentPanel getInstance()
{
if(instance == null)
return instance = new ContentPanel();
else
return instance;
}
}
and ..
public class FirstPanel extends Composite implements HistoryListener {
private static FirstPanel instance;
private VerticalPanel panel;
public FirstPanel() {
History.addHistoryListener(this);
panel = new VerticalPanel();
panel.setStyleName("panelstyle");
Button button2 = new Button("Next page");
button2.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
History.newItem("second_page");
}
});
panel.add(button2);
initWidget(panel);
}
public static FirstPanel getInstance()
{
if(instance == null)
return instance = new FirstPanel();
else
return instance;
}
public Widget getWidget() {
return panel;
}
public void onHistoryChanged(String historyToken) {
if(historyToken.equalsIgnoreCase("second_page"))
ContentPanel.getInstance().setContent(SecondPanel.getInstance());
}
}
and the last but not least :))
public class SecondPanel extends Composite implements HistoryListener
{
private static SecondPanel instance;
public SecondPanel()
{
History.addHistoryListener(this);
VerticalPanel verticalPanel = new VerticalPanel();
TextBox firstnameBox = new TextBox();
TextBox lastnameBox = new TextBox();
Button submitButton = new Button("Click");
verticalPanel.add(firstnameBox);
verticalPanel.add(lastnameBox);
verticalPanel.add(submitButton);
submitButton.addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
History.newItem("first_panel");
alert("You are in second panel");
}
});
initWidget(verticalPanel);
}
public static SecondPanel getInstance()
{
if(instance == null)
return instance = new SecondPanel();
else
return instance;
}
public void onHistoryChanged(String historyToken)
{
if(historyToken.equalsIgnoreCase("first_panel"))
ContentPanel.getInstance().setContent(FirstPanel.getInstance());
}
}
The problem is that i click the button in FirstPanel and SecandPanel is loaded then press "Back" in the browser does not do anything.In onHistoryChanged method the param historyToken is empty string, should't be a value from the stack (first_page)?
I will be grateful if someone find the problem or explain me where i do mistakes.
Thanks
On first page load you are calling onHistoryChanged(INIT_STATE) by hand. This does not change history. Replace with this:
public FirstPanel() {
History.addHistoryListener(this);
String token = History.getToken();
if (token.length() == 0) {
History.newItem(INIT_STATE);
} else {
History.fireCurrentHistoryState();
}
.. rest of code
Better practice would be to register History listeners History.addHistoryListener(..) only in the top-most panel (EntryPoint or ContentPanel) and switch panels based on history from there.
Related
i have a program with a frame that contains a main panel with the cardlayout layout, and i want it to display different cards/panel.
In my case i'm really struggling to call a new card from a button action listener.
I want a new card to appear after i click on a button but none of the codes i put in my action listener displayed the card i wanted.
I know my actionListener work because i did a println inside.
here's my code. i got rid of anything that was unnecessary so it's easier to read. Thanks for the help!
i'll take all advices about code structuration
the mainFrame :
public class MainFrame extends JFrame{
final static String CONNEXION_VIEW = "connexionView";
final static String CONNEXION_FAILED_VIEW = "connexionRefusee";
public MainFrame()
{
super();
initialize();
}
private void initialize()
{
getMainPanel();
add(getMainPanel());
}
CardLayout cardLayout;
public CardLayout getCardLayout()
{
if (cardLayout == null)
{
cardLayout = new CardLayout();
}
return cardLayout;
}
JPanel mainPanel;
public JPanel getMainPanel()
{
if (mainPanel == null)
{
mainPanel = new JPanel();
mainPanel.setLayout(getCardLayout());
mainPanel.add(CONNEXION_VIEW, getConnexionView());
mainPanel.add(CONNEXION_FAILED_VIEW, getConnexionFailedView());
}
return mainPanel;
}
ConnexionView connexionView;
protected ConnexionView getConnexionView()
{
if (connexionView == null)
{
connexionView = new ConnexionView();
}
return connexionView;
}
ConnexionFailedView connexionFailedView;
protected ConnexionFailedView getConnexionFailedView()
{
if (connexionFailedView == null)
{
connexionFailedView = new ConnexionFailedView();
}
return connexionFailedView;
}
the connexion view, the one with the button to click with the action listener where i want to put my code
public class ConnexionView extends JPanel{
GridBagLayout gbl = new GridBagLayout();
private JButton btnConnexion;
Dimension dimensionBouton = new Dimension(170, 30);
public ConnexionView()
{
super();
initialise();
}
private void initialise()
{
setLayout(gbl);
GridBagConstraints gbcbtnConnexion = new GridBagConstraints();
gbcbtnConnexion.gridwidth = GridBagConstraints.REMAINDER;
gbcbtnConnexion.gridheight = GridBagConstraints.REMAINDER;
gbcbtnConnexion.gridx = 1;
gbcbtnConnexion.gridy = 2;
add(getBtnConnexion(), gbcbtnConnexion);
}
private JButton getBtnConnexion()
{
if (btnConnexion == null)
{
btnConnexion = new JButton("Connexion");
btnConnexion.setPreferredSize(dimensionBouton);
btnConnexion.setMinimumSize(dimensionBouton);
btnConnexion.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
/////code to display the connexion_Failed_View
System.out.println("test");
}
});
}
return btnConnexion;
}
}
and the connexion failed view, the one i want to display after the button is clicked
public class ConnexionFailedView extends JPanel{
public ConnexionFailedView()
{
super();
initialise();
}
private void initialise()
{
setBackground(Color.YELLOW);
}
thanks in advance
You will need to keep the component in your Button somehow, so you can access it.
class ConnexionView {
private JComponent mainPanel;
public ConnexionView(JComponent mp) { mainPanel = mp; }
}
Obviously, that means the MainFrame needs to pass it then.
Now, the listener can do
// it would be cleaner if you passed the layout in the constructor as well
CardLayout cl = (CardLayout) mainPanel.getLayoutManager();
cl.show(mainPanel, MainFrame.CONNEXION_FAILED_VIEW);
I've tried to apply the Observable/Observer pattern but there is something wrong with my code when I try to change a the textfield of a JTextPane.
I've got 3 classes, Play, Controller and SecondWindow here are a sample of their code.
public class Play() {
Controller c = new Controller();
SecondWindow sw = new SecondWindow();
c.addObserver(sw)
c.setText("blabla");
}
My class Controller:
public class Controller extends Observable(){
private String text ="";
private static Controller getInstance() {
if (instance == null) {
instance = new Controller();
}
return instance;
}
public void setText(String s) {
text = s;
setChanged();
notifyObservers();
}
}
and SecondWindow:
public class SecondWindow extends JFrame implements Observer{
private JPanel contentPane;
private Controller c;
private JTextPane txt = new JTextPane();
public SecondWindow () {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SecondWindow frame = new SecondWindow();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SecondWindow() {
initComponents();
createEvents();
c = Controller.getInstance();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(1000, 0, 300,500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
txt.setBounds(0, 0, 280, 460);
txt.enable(false);
contentPane.add(txt);
}
public void update(Observable arg0 , Object arg1){
// Things to change here
}
I can't manage to put the variable c in the textField (like a txt.setText(c.getText) instruction). I'm sure that it reads the method update, but I don't know how to make sure it works.
Hint: Per the Observerable API the notifyObservers method has an overload that accepts any object as a parameter:
public void notifyObservers(Object arg)
This can even be a String. And as per the Observer API, this object is then passed into the update method in the observer, and you can use it there.
void update(Observable o,
Object arg)
arg - an argument passed to the notifyObservers method.
Separate side issue here:
contentPane.setLayout(null);
For most Swing aficionados, seeing this is like hearing nails on a chalkboard -- it's painful. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Side issue number two: your code is not Swing thread safe, since the Swing GUI could very well be notified by the observable off of the Swing event dispatch thread or EDT. While it is not likely to cause frequent or serious problems with this simple program, in general it would be better to use a SwingPropertyChangeSupport and PropertyChangeListeners rather than Observer / Observable if you can.
Next Side Issue
This:
public class Controller extends Observable(){
isn't compilable / kosher Java. Same for the duplicate parameter-less constructors for the SecondWindow class. Yes, we know what you're trying to do, but it's hard enough trying to understand someone else's code, you really don't want to make it harder by posting kind-of sort-of uncompilable code, trust me.
For example, something simple could be implemented in Swing using PropertyChangeListeners, like so:
import java.util.concurrent.TimeUnit;
public class Play2 {
public static void main(String[] args) {
Model2 model2 = new Model2();
View2 view2 = new View2();
new Controller2(model2, view2);
view2.show();
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// one of the few times it's OK to ignore an exception
}
String text = String.format("Counter Value: %d", i);
model2.setText(text);
}
}
}
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Model2 {
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
public static final String TEXT = "text"; // name of our "bound" property
private String text = "";
public String getText() {
return text;
}
public void setText(String text) {
String oldValue = this.text;
String newValue = text;
this.text = text;
pcSupport.firePropertyChange(TEXT, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(name, listener);
}
}
import javax.swing.*;
public class View2 {
private JPanel mainPanel = new JPanel();
private JTextField textField = new JTextField(10);
public View2() {
textField.setFocusable(false);
mainPanel.add(new JLabel("Text:"));
mainPanel.add(textField);
}
public JPanel getMainPanel() {
return mainPanel;
}
public void setText(String text) {
textField.setText(text);
}
public void show() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("View");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Controller2 {
private Model2 model2;
private View2 view2;
public Controller2(Model2 model2, View2 view2) {
this.model2 = model2;
this.view2 = view2;
model2.addPropertyChangeListener(Model2.TEXT, new ModelListener());
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
view2.setText((String) pcEvt.getNewValue());
}
}
}
It has been a while since the last time I did Swing programming, and today I'm getting back to it. I have a simple JList which is backed by DefaultListModel. I also have a JButton which will show a JFileChooser. When a directory is selected, the JList is supposed to be populated with the file names under the selected directory.
What I found is that occasionally (actually it happens randomly quite often), the list wont be updated until I click on the (seemingly blank) list. I thought by using DefaultListModel, I can just call addElement() which will trigger the fireIntervalAdded (which should repaint the list, the container, etc) ? ALso, I believe the actionPerformed() method is invoked inside the EDT, so I should just be able to update the DefaultListModel. Anyway.... I have also tried calling revalidate() and repaint() on the list, the container, etc without any success either.
Secondly, when the list already has some items in it, clicking the button (which triggers the filechooser to be shown) will clear up the JList entries (without calling clear() on the model).
The source code is available at:
https://github.com/alexwibowo/spider
Here is an abstract of the code (hopefully it is sufficient)
package org.github.alexwibowo.spider.gui;
import com.jgoodies.forms.factories.CC;
import com.jgoodies.forms.layout.FormLayout;
import javax.swing.*;
public class MainPanel extends JPanel {
public MainPanel() {
initComponents();
}
private void initComponents() {
toolBar1 = new JToolBar();
openFolderButton = new JButton();
splitPane1 = new JSplitPane();
scrollPane1 = new JScrollPane();
fileList = new JList();
//======== this ========
setLayout(new FormLayout(
"default:grow",
"default, $lgap, fill:default:grow"));
//======== toolBar1 ========
{
toolBar1.setFloatable(false);
//---- openFolderButton ----
openFolderButton.setIcon(UIManager.getIcon("Tree.openIcon"));
openFolderButton.setBorder(new EmptyBorder(5, 5, 5, 5));
toolBar1.add(openFolderButton);
}
add(toolBar1, CC.xy(1, 1));
//======== splitPane1 ========
{
//======== scrollPane1 ========
{
//---- fileList ----
fileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
scrollPane1.setViewportView(fileList);
}
splitPane1.setLeftComponent(scrollPane1);
}
add(splitPane1, CC.xy(1, 3));
}
protected JToolBar toolBar1;
protected JButton openFolderButton;
protected JSplitPane splitPane1;
protected JScrollPane scrollPane1;
protected JList fileList;
}
and the panel which extends the above. This is the class which handles the addition of filenames to the list :
package org.github.alexwibowo.spider.gui
import javax.swing.*
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
class BarcodeMainPanel extends MainPanel {
private DefaultListModel<String> listModel = new DefaultListModel<String>()
BarcodeMainPanel() {
initModels()
initEventHandling()
}
protected void initModels() {
fileList.model = listModel
}
protected void initEventHandling() {
openFolderButton.addActionListener(new ActionListener() {
#Override
void actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setLocation(50, 50);
if (chooser.showOpenDialog(BarcodeSpiderMainFrame.instance()) == JFileChooser.APPROVE_OPTION) {
listModel.clear()
File selectedDirectory = chooser.getSelectedFile()
selectedDirectory.eachFile {
listModel.addElement(it.name)
}
} else {
System.out.println("No Selection ");
}
}
})
}
}
The frame which contains the panel (just for completeness) :
package org.github.alexwibowo.spider.gui
import groovy.transform.Synchronized
import javax.swing.*
import java.awt.*
class BarcodeSpiderMainFrame extends JFrame{
private static BarcodeSpiderMainFrame INSTANCE;
BarcodeSpiderMainFrame(String title) throws HeadlessException {
super(title)
}
#Synchronized
public static BarcodeSpiderMainFrame instance() {
if (INSTANCE == null) {
INSTANCE = new BarcodeSpiderMainFrame("Spider")
INSTANCE.minimumSize = new Dimension(800,600)
INSTANCE.maximumSize = new Dimension(1024,768)
INSTANCE.defaultCloseOperation = EXIT_ON_CLOSE
}
INSTANCE.initializeContent()
INSTANCE.visible = true
INSTANCE
}
private void initializeContent() {
BarcodeMainPanel mainPanel = new BarcodeMainPanel()
this.contentPane.add(mainPanel);
}
}
and finally the launcher (just for completeness) :
package org.github.alexwibowo.spider
import org.github.alexwibowo.spider.gui.BarcodeSpiderMainFrame
import javax.swing.*
#Singleton
class SpiderLauncher {
BarcodeSpiderMainFrame barcodeSpiderMainFrame
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SpiderLauncher.instance.run(args);
}
});
}
void run(String[] args) {
barcodeSpiderMainFrame = BarcodeSpiderMainFrame.instance()
barcodeSpiderMainFrame.show()
}
}
This is what fixes it.
In BarcodeSpiderMainFrame, remove the call to setVisible. So it will look something like:
public static BarcodeSpiderMainFrame instance() {
if (INSTANCE == null) {
INSTANCE = new BarcodeSpiderMainFrame("Spider")
INSTANCE.minimumSize = new Dimension(800,600)
INSTANCE.preferredSize = new Dimension(1024,768)
INSTANCE.maximumSize = new Dimension(1024,768)
INSTANCE.defaultCloseOperation = EXIT_ON_CLOSE
}
INSTANCE.initializeContent()
// INSTANCE.visible = true // remove this line
INSTANCE
}
and in the launcher, call setVisible()
#Singleton
class SpiderLauncher {
BarcodeSpiderMainFrame barcodeSpiderMainFrame
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
SpiderLauncher.instance.run(args);
}
});
}
void run(String[] args) {
barcodeSpiderMainFrame = BarcodeSpiderMainFrame.instance()
barcodeSpiderMainFrame.pack()
barcodeSpiderMainFrame.setVisible(true) // add this line
}
}
I have added the call to pack(). But i dont think it really matters. How did the above fix my problem? I do not know. It would be great if someone can explain what actually happened.
I have problem in understanding a composite.
In Frame class i have verticalPanel(vp) when vp is loaded , getAction and get Button is visible on vp
WHen click on button there is a getTree is executed and there is treeC class initialized where there is customised tree. and treeitem.
I want to use action object in class TreeC
How to do it.
Plz Help.
public class Frame{
public frame () {
initWidget(getFramePanel());
}
Private VerticalalPanel getFramePanel() {
if (vp== null) {
vp= new VerticalalPanel();
vp.setSize("1442px", "750px");
vp.add(getAction());// **are composites**
vp.add(getButton) // **are composite**
}
return vp;
private Action getAction() {
if (action == null) {
action = new Action(); // In action class there are 7 buttons and 2 methods //setDisplayRepository(), and setDisplayFolder()
action.setDisplayRepository();
}
return action;
}
}
private Button getButton() {
if (btn == null) {
btn = new Button("Click");
btnProperties.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
hp.add(getTree());
}
});
btn.setSize("37px", "36px");
}
return btnProperties;
}
private TreeCmis getTreeC() {
if (treeC == null) {
treeC = new TreeC();
treeC.setWidth("360px");
}
return treeCmis;
}
}
public class TreeC extends Composite{
private Tree repo;
//constructor
public TreeC {
createTree()
}
Void createTree(){
/* here i need to to use the object action declared in frame class
For using action.setDisplayfolder*/
}
}
The simplest way is:
public class TreeC extends Composite{
private Tree repo;
private Action action;
//constructor
public TreeC(Action action) {
this.action = action;
createTree()
}
void createTree(){
/* here i need to to use the object action declared in frame class
For using action.setDisplayfolder*/
}
}
When Create instance treeC = new TreeC(action);
I've got the following button...
public void actionPerformed(ActionEvent arg0) {
Contacts contact = new Contacts();
contact.setVisible(true);
}
Contacts is just a simple JApplet...
public class Contacts extends JApplet {
private JPanel jContentPane = null;
public Contacts() {
super();
}
public void init() {
this.setSize(500, 260);
this.setContentPane(getJContentPane());
}
private JPanel getJContentPane() {
if (jContentPane == null) {
jContentPane = new JPanel();
jContentPane.setLayout(null);
jContentPane.add(getJList(), null);
jContentPane.add(getJButton(), null);
jContentPane.add(getJButton1(), null);
}
return jContentPane;
}
}
Why is what I'm doing not working? How would I launch this JFrame?
Like JFrame and JDialog, JApplet is a top-level container. You can't put one inside the other. Instead, do something like this:
class Contacts extends JFrame { ... }
...
Contacts contact = new Contacts();
contact.setVisible(true);
If you have an existing JApplet that you want to display in a JFrame, you can create a hybrid, as shown in the examples examined here.