Wicket AjaxIndicatorAppender across panels - java

I have 2 panels on one page and in first panel I call Ajax link and do some back end operations with long duration. In another panel I want show AjaxIndicatorAppender.
If I use AjaxIndicatorAppender with listener in the first panel, everything work fine, but if I use it in panel 2, it does nothing. Any help how call Ajax in panel 1 and show the Ajax indicator in panel 2? I am lost.
In panel1 (menu panel):
modal1.setWindowClosedCallback(new ModalWindow.WindowClosedCallback()
{
#Override
public void onClose(AjaxRequestTarget target)
{
some code.....
send(getPage(), Broadcast.DEPTH, new ContentUpdate(target));
etc...
in panel2 (content panel)
#Override
public void onEvent(IEvent<?> event) {
super.onEvent(event);
if (event.getPayload() instanceof ContentUpdate) {
AjaxRequestTarget target = ((ContentUpdate)event.getPayload()).getTarget();
ContentSection section = new ContentSection("content0",contentView.getCustomSectionList().get(0));
listItems.get("content0").replaceWith(section);
listItems.get("pagingNavigator0").replaceWith(new PagingNavigator("pagingNavigator0", section.getDataView()));
target.add(wmc);
}
}
How can I trigger listener in panel 2?
Thank you

I fixed this problem. My parent page can trigger IAjaxIndicatorAware too. I move this interface to my parent page and I call IndicatorMarkupId from the second panel.
Thank for help :)

The AjaxLink should implement IAjaxIndicatorAware interface and #getAjaxIndicatorMarkupId() should return the markup id of the HTML element with the loading indicator in Panel2.
But this way you would couple Panel1 with Panel2. Since you use Wicket Event mechanism I guess you try to keep them decoupled.
Probably you need to show a page-wide loading indicator instead.

Related

Panel background not being changed by UI manager

In my program I would like the user to be able to change the colour scheme of the program. I have a method which passes in a colour to set the background of all the panels using UIManager.
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(this);
}
However the issue I'm running into is that it is not changing the colour of the panels. This method is located in the class for the JFrame.
Copied directly from Swing API Docs.
public static void updateComponentTreeUI(Component c)
A simple minded look and feel change: ask each node in the tree
to updateUI() -- that is, to initialize its UI property with the
current look and feel.
Notice the emphasize. Your code doesn't work because you are passing this as the argument to updateComponentTreeUI(). Since you are passing your panel as the argument, only the components inside the panel and the panel itself will get their UI updated. You have to pass the container that holds all other panels in your program, that is your JFrame
public void changeColourScheme(Color c) {
UIManager.put("Panel.background", c);
SwingUtilities.updateComponentTreeUI(frame); //instace of your frame
}
simply because the UIDefaults changes is only effective for the newly created components
for your feature to work you have to make the app restart then before starting the gui change the background in the defaults in the UIManager , or you will have to do it manually (panel by panel).
if you do it manually recursion will help a lot ,like this
private static void loopForPanel(Container c, Color col) {
synchronized (c.getTreeLock()) {
for (Component com : c.getComponents()) {
if (com instanceof JPanel) {
com.setBackground(col);
}
if (com instanceof Container) {
loopForPanel((Container) com,col);
}
}
}
}
then in your changeColourScheme method pass the frame it self for the loopForPanel method with your desired color.

Swing: restrict keyboard focus traversal to specific widgets

I have a JFrame containing the usual assortment of panels and widgets, and I have a JPanel which I'm using as that JFrame's glassPane. I'd like to be able to restrict keyboard focus traversal to the components in the glassPane when it's visible.
My problem may or may not be compounded by the fact that a background thread launches a process which results in a progress dialog appearing and subsequently disappearing, which steals the focus from a widget in my glassPane but returns it to some widget beneath my glassPane.
I've tried setting the JFrame's focus traversal policy to one which only allows the glassPane to be focused, but that didn't seem to have any effect. (maybe I did it incorrectly?)
Any help would be appreciated.
As mentioned in my comment, I would probably go for a J/X/Layer - but here's a solution if indeed a custom FTP is the only missing piece in your context.
To exclude components from focus traversal, reject them in the accept method of a custom FTP. Below is an example which rejects all components which are not children of the glassPane if the glassPane is visible (note: this is overly simplistic, as it handles direct children only, real-world code must walk up the parent chain until it either hit the glasspane or not)
public static class FTP extends LayoutFocusTraversalPolicy {
#Override
protected boolean accept(Component comp) {
JFrame window = (JFrame) SwingUtilities.windowForComponent(comp);
if (hasVisibleGlassPane(window)) {
return comp.getParent() == window.getGlassPane();
}
return super.accept(comp);
}
private boolean hasVisibleGlassPane(JFrame window) {
return window != null && window.getGlassPane() != null
&& window.getGlassPane().isVisible();
}
}
I'm going to make my comment an answer as I believe that it is the solution to your problem:
Perhaps a better option is to use a CardLayout to simply swap views rather than a glass pane or dialog since this situation does not seem the best suited for glass pane use. If you use CardLayout, you don't have to come up with a kludge (fiddle with focus traversal), to fix the side effects of another kludge (using glass pane for things it wasn't intended to be used for).
If you're not familiar with it, a CardLayout will allow you to easily swap components in a GUI, and is often used to swap JPanels that hold complex GUI's that occurs when the user goes from one major program state to another. I think that it would be perfect for your purposes and would prevent you from having to worry about your focus issue.
Try calling .setFocusable(false) on the JFrame you want to ignore (or at worst, on all the components within it) while the glassPane is visible.
When I had discussion with client he gave me the same requirement. So I decided to use the JLayer and LayeredPane in my project and with all of these components as a simple solution I implemented following code, may be it will help you in your project.
public YourConstructor() {
yes.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent fe) {
if (!no.hasFocus()) {
no.requestFocusInWindow();
}
}
});
no.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent fe) {
if (!yes.hasFocus()) {
yes.requestFocusInWindow();
}
}
});
}
#Override
public void setVisible(boolean visibility) {
super.setVisible(visibility);
if (visibility) {
yes.requestFocusInWindow();
}
}
To build upon kleopatra's answer and to help others in a similar situation; I had a bunch of custom swing components that I didn't want focused (sometimes only when they weren't editable). I ended up with this:
/**
* A custom focus traversal policy to make focus traversal inside a container to ignore some swing components.<br /><br />
*
* <b>Ignored components:</b><br />
* - <code>CustomComponent1</code> components<br />
* - <code>CustomComponent2</code> components that are not editable<br /><br />
*
* <b>Usage:</b><br /><br />
* <code>Container.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());</code>
*/
public class CustomFocusTraversalPolicy extends LayoutFocusTraversalPolicy {
private static final long serialVersionUID = 1L;
protected boolean accept(Component c) {
if(c instanceof CustomComponent1) {
return false;
}
if(c instanceof CustomComponent2) {
CustomComponent2 t = (CustomComponent2) c;
if(!t.isEditable()) {
return false;
}
}
return super.accept(c);
}
}
Note that the policy needs to be set for each Container (I did it for each Window I was creating):
Window window = new JFrame(); // Or JDialog; both subclasses of Container and Window
window.setFocusTraversalPolicy(new CustomFocusTraversalPolicy());

Java swing -- Jpanel not rerendering/repainting itself

Im using a JPanel with propertyChangeListener and want it to rerender itself based on whenever a particular variable model changes. My code for the same is as follows --
public class LabelMacroEditor extends JPanel implements PropertyChangeListener {
private static final long serialVersionUID = 1L;
private LabelMacroModel model;
public LabelMacroEditor(LabelMacroModel bean) {
this.model = bean;
model.addPropertyChangeListener(this);
setupComponents();
validate();
setVisible(true);
}
public void setupComponents()
{
Box allButtons = Box.createVerticalBox();
JScrollPane macroModelScroller = new JScrollPane(allButtons);
macroModelScroller.setPreferredSize(new Dimension(300, 200));
for(MacroModel macroModel : model.getMacroModelList())
{
LabelMacroEditorEditableEntity macroEditorEntity = new LabelMacroEditorEditableEntity(macroModel);
Box entityBox = Box.createHorizontalBox();
entityBox.add(macroEditorEntity.getUpButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getMacroDetailsButton());
entityBox.add(Box.createHorizontalStrut(15));
entityBox.add(macroEditorEntity.getDownButton());
allButtons.add(entityBox);
}
add(macroModelScroller);
}
#Override
public void propertyChange(PropertyChangeEvent arg0) {
revalidate();
repaint();
}
}
When i use the debug mode in eclipse i can see that whenever there is a change to model it triggers off the call propertyChange and it also runs over revalidate and repaint but only the JPanel display remains the same. It does not seem to be rerendering itself.
Anything fundamental that I'm missing here ?
EDIT :
An example snippet of a property im changing is as follows --
labelMacroModel.addMacroModel(addedMacroModel);
where labelMacroModel is of the type LabelMacroModel and addedMacroModel is of the type Macro
Now the relevant part of LabelMacroModel class that fires off the property change is as follows --
private List<MacroModel> macroModelList;// this is the list of all MacroModels
public void addMacroModel(MacroModel macroModel) {
macroModelList.add(macroModel);
pcs.fireIndexedPropertyChange("LabelMacroModel", macroModelList.size(), null, macroModel);
}
Its not clear how you are changing the components in the panel. If panel is not updated then repaint/revalidate will have no effect. I think you should not need revalidate/repaint to be called explicitly if you are not modifying the way components are laid out. JButton.setText should for example change the label of the button without need of calling repaint.
To expand on the answer by AKJ above, I think you should be reconstructing your components on property change. So doing a remove all then readding is one way to do this. Once you get this working you could be more selective about pushing the model update into the GUI eg if a new entry has been added then just add a new component to reflect this. The remove all / readd is fine for a lot of cases though. HTH.

In Model-View-Controller, Why change in model, doesn't trigger the change in view?

I am currently developing my own minesweeper. Swing follows Model-View-Controller design pattern. In MVC, I learnt whenever there is a change in model, the controller will trigger that change in view too. But In this example, I cannot trace how to make the changes in setTitle and setInfo to get reflected in view.
Here, when I set the title of the Dialog box, the actual content(model) is getting changed, But there is no corresponding change in the output(view).
//InfoDisplayer is inner class of class MenuActionListener
class InfoDisplayer extends JDialog {
JLabel info;
BorderLayout infoBorderLayout = new BorderLayout();
public InfoDisplayer(JFrame ownerFrame) {
super(ownerFrame,true);
info = new JLabel();
setFocusable(false);
setSize(300,400);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setLayout(infoBorderLayout);
add(info,BorderLayout.SOUTH);
setVisible(true);
}
void setInfo(JLabel info) {
this.info = info;
}
public void setTitle(String title) {
super.setTitle(title);
}
}
if ((event.getActionCommand()).equals("HowToPlay")) {
InfoDisplayer instructionsDisplay = new InfoDisplayer(gUIManagerFrame);
//gUIManagerFrame is an object of its outer class,MenuActionListener
instructionsDisplay.setTitle("INSTRUCTIONS");
instructionsDisplay.setInfo(new JLabel("<html><h1><B>INSTRUCTIONS</B></h1></html>"));
} else {// if about is clicked!!
InfoDisplayer aboutDisplay = new InfoDisplayer(gUIManagerFrame);
aboutDisplay.setTitle("MineSweeper v0.1");
aboutDisplay.setInfo(new JLabel("<html><h1><B>MineSweeperv1.0</B></h1> </html>"));
}
Whenever there is a change in model, the controller will trigger that change in view.
In the Model–View–Controller pattern, when the controller updates the model, the model will notify the view, typically using the observer pattern, and the view then updates itself. The view may interrogate the model and process any resulting update. There's a more detailed answer and example here.
You will need to remove the old jlabel and add the new one to the frame.
Though it would make more sense probably to set the text on the existing label rather than a whole new label.
Swing indeed has a model and a view side. For example in a JTable the JTable is the view and the TableModel is the model. When you construct a JTable, you need to pass it a model either during construction or by using the setter. The JTable will then add a listener to model to get informed about any model changes. You can see this listener as the controller.
However, this does not mean that when you use an arbitrary combination of Swing classes they will auto-magically get informed about each other changes. In your case, the label is certainly not 'the model' of your dialog, and there is no such thing as a 'controller' between your label and the dialog. When you make such a change, you need to inform the dialog yourself (and probably add the label to your dialog as well).
Oh, and I would recommend changing your setTitle method into
public void setTitle( String aTitle ){
super.setTitle( aTittle );
}
or remove it completely. This will avoid a StackOverflowException

How to access multiple JPanels inside JFrame?

I have a JFrame that contains a "display" JPanel with JTextField and a "control" JPanel with buttons that should access the contents of the display JPanel. I think my problem is related on how to use the observer pattern, which in principle I understand. You need to place listeners and update messages, but I don't have a clue where to put these, how to get access from one panel to the other and maybe if necessary to introduce a "datamodel" class. For example, I want to access the contents of the JTextField from the control panel and I use an anonymous action listener as follows:
JButton openfile = new JButton("Convert file");
openfile.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openButtonPressed();
}
});
You need to reduce the coupling between these objects.
You can have a master object, that owns all the text fields and the button ( the panels are irrelevant )
Then a separete actionlistener within that master object ( I call it mediator see mediator pattern )
That action listener performs a method on the mediator which in turn take the values from the textfields and create perhaps a transfer object.
This way you reduce the coupling between the panels, textfields etc. and let the control in one place ( the mediator ) that is, you don't let them know each other.
You can take a look at the code in this question:
https://stackoverflow.com/questions/324554/#324559
It shows these concepts in running code.
BTW the observer pattern is already implemented in the JTextField, JButton, ActionListener etc. You just need to add the hooks.
I hope this helps.
EDIT Joined two answers into one.
This is the code.
class App { // this is the mediator
// GUI components.
private JFrame frame;
private JTextField name;
private JTextField count;
private JTextField date;
// Result is displayed here.
private JTextArea textArea;
// Fired by this button.
private JButton go;
private ActionListener actionListener;
public App(){
actionListener = new ActionListener(){
public void actionPerformed( ActionEvent e ){
okButtonPressed();
}
};
}
private void okButtonPressed(){
// template is an object irrelevant to this code.
template.setData( getData() );
textArea.setText( template.getTransformedData() );
}
public void initialize(){
frame = new JFrame("Code challenge v0.1");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
name = new JTextField();
count = new JTextField();
date = new JTextField();
textArea = new JTextArea();
go = new JButton("Go");
go.addActionListener( actionListener ); // prepare the button.
layoutComponents(); // a lot of panels are created here. Irrelevant.
}
}
Complete and running code can be retrieved here:
It is important to favor composition over inheritance when possible.
It does make the code cleaner if you create the models in one layer and add a layer or two above to create the components and layout. Certainly do not extend the likes of JFrame and JPanel.
Do not feel the need to make the composition hierarchy in the model layer exactly match the display. Then it's just a matter of taking the text from the Document and performing the relevant operation.
Okay, perhpas not that simple. Swing models are a little bit messy. In particular ButtonModel is brain damaged, and the controller area of code might not be entirely pure.
We have so called builders, which will build the parent panel out of the children. In this builder you will have access to all the subcomponents you need to listen to and can thus can implement any logic there.
Finally the builder will then return the parent panel with the complete logic.
Once you've got the parent panel it's really a mess getting to the child components and have them do anything.
thanks. I added a datamodel layer which handles somehow the communication between the panels.
I also found this link on Listeners on JTextField usefull:
link text

Categories