views:

72

answers:

1

Hello!

I posted this question originally in the MigLayout forum since it is to some extend specific for that specific of Layout Manager, I'd say. Unfortunately it's a week old now without any comments at all, thus I'd like to repost the question here, in hope it's not considered X-posting.

Well, I have the following problem: In general I'd like to 'grey out' some content (the content of a JPanel more precisely). I nearly managed to achieve the desired effect via placing another JPanel 'flowing' over the desired area with a transparent colour. There's one major problem left though: Once I set the overlay panel visible, the components below will not immediately paint correct. Instead they will initially paint with some white colour, until for example I minimize and re-maximize the frame which would cause the transparent effect to be drawn correct.

As example I attached a screen-shot of the 'buggy' appearance. Mind the radio buttons and check boxes with their white appearance. Additionally I attached code to reproduce the effect. Currently I'm working on Windows 7, 32bit with Java 1.5. (Java 1.6 didn't make any difference).

I played around quite a lot with the various paint()/repaint()/(in/re)validate()/etc. methods. Nothing worked to paint the overlay panel correct right after setting it visible.

Any ideas?

BR, Chris

EDIT1: Regarding the code sample - if you run it you have to click the "check" button to trigger the overlay panel. :) Another thing which I noticed is that the appearance problems differ based on the UI L&F used. In my case I used once Windows and once the native Java UI (is it called metal?) and they showed different results, even though none of them worked properly :P

Link for a screenshot of the bug: Screenshot

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import net.miginfocom.swing.MigLayout;

public class MigTest {
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.add(new TestLayeredPane());
    f.pack();
    f.setVisible(true);
  }
  public static class TestLayeredPane extends JPanel {
    private JPanel pnl_overlay_;

public TestLayeredPane() {
  super();
  this.setLayout(new MigLayout("fill, wrap 2", "", ""));

  initLayout();
}
private void initLayout() {
  JPanel pnl_direction = new JPanel(new MigLayout("wrap 3", "", ""));
  JPanel pnl_settings = new JPanel(new MigLayout("wrap 1", "", ""));
  JPanel pnl_input = new JPanel(new MigLayout("wrap 3", "", ""));

  JLabel lbl_ask_for = new JLabel();
  JLabel lbl_translation = new JLabel();

  ButtonGroup btn_group_ = new ButtonGroup();
  JRadioButton radiobtn_ask_lang1_ = new JRadioButton();
  JRadioButton radiobtn_ask_lang2_ = new JRadioButton();
  JRadioButton radiobtn_ask_random_ = new JRadioButton();

  JCheckBox chkbox_loop_ = new JCheckBox();
  JCheckBox chkbox_repeat_false_ = new JCheckBox();
  JCheckBox chkbox_letter_count_ = new JCheckBox();
  JCheckBox chkbox_ask_all_ = new JCheckBox();

  JLabel lbl_progress_ = new JLabel();
  JLabel lbl_question_ = new JLabel();

  JButton btn_check_ = new JButton();
  JButton btn_push_back_ = new JButton();

  JTextField tfield_answer_ = new JTextField();

  /** Customize all elements of our layout **/
  lbl_ask_for.setText("Ask for:");

  lbl_translation.setText("translation");

  radiobtn_ask_lang1_.setText("1st language");
  radiobtn_ask_lang2_.setText("2nd language");
  radiobtn_ask_random_.setText("Random language");

  btn_group_.add(radiobtn_ask_lang1_);
  btn_group_.add(radiobtn_ask_lang2_);
  btn_group_.add(radiobtn_ask_random_);

  chkbox_loop_.setText("Loop");
  chkbox_repeat_false_.setText("Repeat false answers");
  chkbox_letter_count_.setText("Show letter count");
  chkbox_ask_all_.setText("Ask for 1st and 2nd language");

  btn_check_.setText("Check");
  btn_check_.addActionListener(new ActionListener() {

    public void actionPerformed(ActionEvent e) {
      finishTraining();
    }
  });

  btn_push_back_.setText(" Push back");

  /** Add all elements to this panel **/
  pnl_direction.add(lbl_ask_for, "span 1 3");
  pnl_direction.add(radiobtn_ask_lang1_);
  pnl_direction.add(lbl_translation, "span 1 3");
  pnl_direction.add(radiobtn_ask_lang2_);
  pnl_direction.add(radiobtn_ask_random_);

  pnl_settings.add(chkbox_loop_);
  pnl_settings.add(chkbox_repeat_false_);
  pnl_settings.add(chkbox_letter_count_);
  pnl_settings.add(chkbox_ask_all_);

  pnl_input.add(lbl_question_, "align center");
  pnl_input.add(lbl_progress_, "span 2, align center");
  pnl_input.add(tfield_answer_, "span 1 2, align center, w 200!");
  pnl_input.add(btn_check_, "growy");
  pnl_input.add(btn_push_back_);

  pnl_overlay_ = new JPanel(new MigLayout("fill", "", ""));
  pnl_overlay_.setBackground(new Color(127, 127, 127, 100));
  this.add(pnl_overlay_, "pos (0%+2px) (0%+2px) (100%-2px) (100%-2px) ");
  pnl_overlay_.setVisible(false);

  this.add(pnl_direction, "gapbefore push");
  this.add(pnl_settings, "gapafter push");
  this.add(pnl_input, "span 2, gapbefore push, gapafter push");
}

private void finishTraining() {
  //disable all visible items in the content area
  for (Component comp : this.getComponents()) {

    if (comp instanceof JPanel) {
      for (Component comp2 : ((JPanel) comp).getComponents()) {
        comp2.setEnabled(false);
      }
    }
    else {
      comp.setEnabled(false);
    }
  }
  pnl_overlay_.setVisible(true);
}
  }
}
+1  A: 

Without attempting to debug your code, I took a quick search around on the topic of disabling containers and their children. There are essentially two approaches which involve either recursively iterating through the child controls and disabling them or overlaying a disable panel like you are doing.

The recursive option is simple if you don't have to track the previous enabled state of all the children and restore them when you want to re-enable the parent container. A function like this will do it:

static void setChildrenEnabled(Container root, boolean enable)
{
    Component children[] = root.getComponents();
    for(int i = 0; i < children.length; i++)
    {
        children[i].setEnabled(enable);
        if(children[i] instanceof Container)
        {
            setChildrenEnabled((Container) children[i], enable);
        }          
    }
}

For the approach like yours, I found what appeared to be a pretty good posting with a decent explanation of the pitfalls and source code which you can find here: http://tips4java.wordpress.com/2009/08/02/disabled-panel/

I hope there is something there that will help.

Arnold Spence
Well:1) disabling (setEnabled(false)) is not really the problem. You mentioned an approach, if needed that could be backed by a map to save the initial state.2) the problem with the glass pane - AFAIK - is that it's only available for root panes and wraps over all containers within its containment hierarchy. thus in my case i would get problems because i use a JInternalFrame added to the pop up layer of the JFrame's layered pane which would be below the glass pane.
ChristianS
I've tried your code and can confirm the behavior. I can also confirm that it seems to be related directly to the MigLayout. If I remove all usages of it and put everything in say, a flow layout, the overlay looks correct. I'll play around with it tomorrow and see if anything turns up :)
Arnold Spence
very kind, thank you! i banged my head against the wall trying to solve this issue without success. maybe some fresh ideas help to come up with a solution :)
ChristianS
I tried a bunch of things, probably the same things you tried, to no avail.
Arnold Spence
the funny thing is that it's fine after e.g. minimize/maximize... :/
ChristianS