JXPanel: define painting behaviour (SWINGX-1547)

[SWINGX-1549] JXPanel: (semi-) transparent background color Created: 27/Feb/13  Updated: 27/Feb/13

Status: Open
Project: swingx
Component/s: Misc Component
Affects Version/s: None
Fix Version/s: 1.6.6

Type: Sub-task Priority: Major
Reporter: kleopatra Assignee: Karl Schaefer
Resolution: Unresolved Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
is related to SWINGX-1516 JXPanel: support _not painting_ backg... Resolved
Tags: JXPanel, opacity, semi-transparent-background

 Description   

Core swing (and currently swingx neither) does not support semi-transparent background colors - due to the well-known opacity catch-22:

  • the background is filled only if isOpaque (note: the getter)
  • reporting isOpaque for not fully opaque colors is illegal, doing so will produce the artefacts

The usual workaround is to set opacity to false and subclass JPanel to take over the background filling before calling super. As JXPanel already takes over much of the background painting (filling and painter), we should support it.

There are two sides of the coin:

  • complying to opacity contract, that is to automatically adjust the opacy to false if the background color is semi-transparent
  • detect and respect the developer's intention whether or not the background should be filled (independent on it's transparency)

We have an old issue (marked as fixed, though I disagree a bit) for the second part in SWINGX-1516.

// first part:
// set semitransparent background and be done
panel.setBackground(PaintUtils.setAlpha(Color.RED, 100); 
// second part: set opacity to relay the intention of filling the 
// background or not
// true: fill background - panel getter takes care of complying to contract
panel.setOpaque(true);
// false: don't fill the background, independent on container alpha or 
// background color
panel.setOpaque(false);

Screenshots of the three-panel example - it's under version control, so can be seen in locally on checkout, in localPath/www/api-reviews/... The screenshots are produced by running JXPanelVisualCheck.backgroundAlphaToggleOpaque with the ui property "JXPanel.patch"
set to Boolean.TRUE.

to be continued ...



 Comments   
Comment by kleopatra [ 27/Feb/13 ]

part of the issue was already adressed (though not really solved) - setting opacity and container/background transparency are orthogonal, the mix'in is an implementation artefact (IMO, might be missing something





[SWINGX-1188] JXTreeTable, JXTable background Created: 10/Oct/09  Updated: 07/Aug/12  Resolved: 07/Aug/12

Status: Resolved
Project: swingx
Component/s: TreeTable
Affects Version/s: 1.6
Fix Version/s: 1.6.5

Type: Task Priority: Major
Reporter: wzberger Assignee: kleopatra
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Operating System: All
Platform: All


Issuezilla Id: 1,188
Tags: jrendererlabel, opacity

 Description   

When setting a solid background color on a JXTable/JXTreeTable the background
color won't appear if the current renderer is a JRendererLabel and if a Synth
based look and feel will be used which defines a default table background.

The funny things is when the table opacity will be changed to false the
background appears as it should. Tested with SyntheticaStandardLookAndFeel.

A workaround is to use an AlternateStripingHighlighter with two equal colors.

Some lines of code to reproduce the issue:

JXTreeTable treeTable = new JXTreeTable(new FileSystemModel());
//on some columns the default table background appears
treeTable.setBackground(new Color(0xC0FFC0));

//highlighter workaround
//Color bg = new Color(0xC0FFC0);
//treeTable.setHighlighters(HighlighterFactory.createAlternateStriping(bg, bg));

JScrollPane scroller = new JScrollPane(treeTable);
getContentPane().add(BorderLayout.CENTER, scroller);

Another LAF independent side effect is that JXTable#setOpaque(false) doesn't
work as expected when a background color is set.



 Comments   
Comment by kleopatra [ 11/Oct/09 ]

hmm ... can't reproduce (on the supported LAFs

But there definitely are issues with Synth - recently I had a hard time to hack around
Nimbus misbehaviour (the forum will turn up my rants <g>), so could well be that Synthetica
is/must be doing something comparable. Issue 1180 has some details and links.

Before digging deeper: what's your jdk and OS? And could you please check against HighlighterClientVisualCheck
(in the renderer test package) - the methods I cant reproduce your problem with contain
"SimpleStriping". If you add Synthetica to the installed LAFs, it should appear as an
option in the SetLAF menu to choose.

Cheers
Jeanette

Comment by kleopatra [ 11/Oct/09 ]

one question I forgot: is that new to post-final swingx? Or the other way round: which
version was the last where it worked, if ever?

Comment by wzberger [ 11/Oct/09 ]

Please find a test case below. The specified background color (Orange) doesn't
appear. Sorry if my previous explanation wasn't clear enough.

public class SynthJXTableTest extends JFrame
{
  private static String synthXml = "<synth>" +
  "  <style id=\"all\">" +
  "    <font name=\"Tahoma\" size=\"11\"/>" +
  "  </style>" +
  "  <bind style=\"all\" type=\"REGION\" key=\".*\"/>" +
  "  <style id=\"table\">" +
  "    <state>" +
  "      <color type=\"BACKGROUND\" value=\"#FFE0E0\" />" +
  "      <color type=\"FOREGROUND\" value=\"#000000\" />" +
  "    </state>" +
  "    <state value=\"SELECTED\">" +
  "      <color type=\"TEXT_BACKGROUND\" value=\"#0070C0\"/>" + 
  "    <color type=\"TEXT_FOREGROUND\" value=\"#FFFFFF\"/>" + 
  "    </state>" +   
  "  </style>" +
  "  <bind style=\"table\" type=\"region\" key=\"Table\"/>" +
  "</synth>";
  
  public static void main(String[] args)
  {
    EventQueue.invokeLater(new Runnable(){
      public void run()
      {
        try
        {
          new SynthJXTableTest();
        }
        catch (Exception e)
        {
          e.printStackTrace();
        }
      }
    });   
  }

  public SynthJXTableTest() throws Exception
  {
    InputStream is = new ByteArrayInputStream(synthXml.getBytes("UTF8"));
    SynthLookAndFeel laf = new SynthLookAndFeel();
    laf.load(is, SynthJXTableTest.class);
    UIManager.setLookAndFeel(laf);    

    JXTable table = new JXTable(10, 5);
    
    //Issue: background color does not appear because JLabelRenderer#isOpaque 
    //returns false - as a result the default table color appears painted by 
    //ComponentUI#update(...)
    table.setBackground(Color.ORANGE);
    
    //very hacky workaround - not recommended - see JLabelRenderer#isOpaque()
    //table.setOpaque(false);
    
    //another workaround
    //table.setUI(new BasicTableUI());
    
    add(new JScrollPane(table));
    
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(new Dimension(600, 400));
    setLocationRelativeTo(null);
    setVisible(true);  
  }  
}  

Comment by kleopatra [ 12/Oct/09 ]

ahhh .. I see, thanks for the example.

But looks like not entirely a SwingX problem - there's something strange in core as well:
painting of the table background without lines isn't table background. And JXTable shows
up correctly if it is not the focused component, harhar ... any ideas? I'm not overly
familiar with synth just burned last time I looked into enhancing support for Nimbus

Below is your example slightly modified to show both side-by-side

CU
Jeanette

/**
 * Issue 1188-swingx: background color of JXTable incorrect for SynthLAF
 */
public class SynthTableColors extends JFrame {
    private static String synthXml = "<synth>" + "  <style id=\"all\">"
            + "    <font name=\"Tahoma\" size=\"11\"/>" + "  </style>"
            + "  <bind style=\"all\" type=\"REGION\" key=\".*\"/>"
            + "  <style id=\"table\">" + "    <state>"
            + "      <color type=\"BACKGROUND\" value=\"#FFE0E0\" />"
            + "      <color type=\"FOREGROUND\" value=\"#000000\" />"
            + "    </state>" + "    <state value=\"SELECTED\">"
            + "      <color type=\"TEXT_BACKGROUND\" value=\"#0070C0\"/>"
            + "    <color type=\"TEXT_FOREGROUND\" value=\"#FFFFFF\"/>"
            + "    </state>" + "  </style>"
            + "  <bind style=\"table\" type=\"region\" key=\"Table\"/>"
            + "</synth>";

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    new SynthTableColors();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public SynthTableColors() throws Exception {
        InputStream is = new ByteArrayInputStream(synthXml.getBytes("UTF8"));
        SynthLookAndFeel laf = new SynthLookAndFeel();
        laf.load(is, SynthTableColors.class);
        UIManager.setLookAndFeel(laf);

        JTable table = new JTable(10, 5);
        configureTable(table);
        add(new JScrollPane(table), BorderLayout.WEST);

        JXTable xtable = new JXTable(10, 5);
        configureTable(xtable);
        add(new JScrollPane(xtable), BorderLayout.EAST);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    /**
     * @param table
     */
    private void configureTable(JTable table) {
        table.setFillsViewportHeight(true);
        // Issue: background color does not appear because
        // JLabelRenderer#isOpaque
        // returns false - as a result the default table color appears painted
        // by
        // ComponentUI#update(...)
        table.setBackground(Color.ORANGE);

        // very hacky workaround - not recommended - see
        // JLabelRenderer#isOpaque()
        // table.setOpaque(false);

        // another workaround
        // table.setUI(new BasicTableUI());
    }
}  

Comment by kleopatra [ 12/Oct/09 ]

more fun below

added

  • boolean column, that is use of checkbox
  • column with a DefaultTableCellRenderer which is not under the control of synth

hmm .... looks fine with Nimbus instead of bare synth, so where else might be the problem?
Could it be that part of the problem of this test case is a not complete enough init?

Anyway, one thing you might try is to change JRendererLabel isOpaque: same as DefaultTableCellRenderer
its implementation is some (maybe not longer needed) performance (assumed? don't know)
optimization which returns a value depending on parents color/opaqueness. If that's changed
to return the label's opaquesness property as set, then the xtable half of the example
looks very much the same as the table's half. If that helps in your concrete context,
then I can try to somehow remove the cleverness from the method, at least for Synth-related
LAFs.

CU
Jeanette

    /**
     * @param table
     */
    private void configureTable(JTable table) {
        // model with boolean to see a checkbox as renderer
        TableModel model = new DefaultTableModel(10, 5) {

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndex == 0) {
                    return Boolean.class;
                }
                return super.getColumnClass(columnIndex);
            }
            
        };
        table.setModel(model);
        // use a renderer which is not under the control of synth
        table.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer());
        // have a portion of table not painted by renderers
        table.setFillsViewportHeight(true);
        // Issue: background color does not appear because
        // JLabelRenderer#isOpaque
        // returns false - as a result the default table color appears painted
        // by
        // ComponentUI#update(...)
        table.setBackground(Color.ORANGE);

        // very hacky workaround - not recommended - see
        // JLabelRenderer#isOpaque()
        // table.setOpaque(false);

        // another workaround
        // table.setUI(new BasicTableUI());
    }
Comment by wzberger [ 12/Oct/09 ]

Here are the results of my investigations.

Generally Synth makes use of the specified background color for a component
state. If no background color is defined a fallback mechanism makes use of the
component background color - Nimbus provides colors in a different way so I
think that's why Nimbus seems to work correct.

You can check this by remarking the default background color definition within
the test case.

But why does normal tables work well? SynthTableCellRenderer is opaque by
default and #isOpaque returns the unchanged opacity. Thats why the user defined
background color appear - even if presented by table cells.

So IMO it would be good for compatibility with Synth to modify
JRendererLabel#isOpaque returning the non-optimized opacity value.

Cheers,
Wolfgang

Comment by kleopatra [ 13/Oct/09 ]

JRendererLabel behaves exactly like DefaultTableCellRenderer when it comes to opaqueness,
so actually I can't see a defect there. SynthTableCellRenderer is ... ehem .... quite
weird and Synth can't cope well with renderers which are not under its control, read:
not replaced by synth.

But the underlying defect is somewhere in SynthTableUI painting code: it does not use
the table's background color to fill the table's background. In the example, you can
see that in the area below the rows: with fillViewportHeight enabled (in java6, for java5
you have to override the table's getSomeThingTracks method to return true if the table
is smaller than the viewport height) that area is painted in the table background color
as defined by the ui, not in the color of table as set in code - that is it's schweinchenrosa,
not orange. Happens if focused only (jdk1.6, didn't check the no longer supported 1.5)

On the other side of the coin, the renderer opaque implementation (both DefaultTablecellRenderer
and JRendererLabel - simple c&p) rely on its opaque parent to paint its own background
as expected and guaranteed. The reasoning is that if the parent is opaque and has the
same background color as the child, then the child need not fill the background - it's
already must be done by the parent.

So, I'm veeery reluctant to change that implementation. Shortly before a release it's
a high risk that I overlook side-effects, JRendererLabel by default is used as rendering
component across all collection components. Taking that risk to cover up for core bugs
is ... hmm ...

I would suggest you file a bug in Sun's bug parade: you can use the left-side of the example
with a core JTable and instead of the JXTable as the right component take anything that's
focusable (as the error shows only on a focused table, the difference might give a hint
where/how exactly the misbehaviour happens).

As to your context - you distribute a tweaked version of SwingX anyway, right? - change
the isOpaque implementation and see how far it holds, I sure can be convinced by evidence
And by masses: start a discussion in the forum, drum up support and pressure <g>

I tend to change this to a task - revisit the implications of changing the implementation
to bare returning the property - and postpone to after 1.6 release.

Thanks
Jeanette

Comment by wzberger [ 13/Oct/09 ]

Yes, I'm aware that it is a Synth issue. So one possibility is to modify the
renderer for Synth based LAF's only. But as soon as this will be fixed (even if
I'm quite sure that it won't get fixed) your renderer has to get fixed too. So I
agree it's better to leave it as it is.

Luckily I'm able to fix the Synth issue under the hood for Synthetica and
everything works fine. Additionally I'll file a bug at Sun's bug database.

Please close the issue.

Thanks,
Wolfgang

Comment by kleopatra [ 13/Oct/09 ]

changed type and target milestone - basically, I fully agree that rendering
components shouldn't try to be too clever. So will change once I checked the
implications for existing clients (internal and external).

Thanks
Jeanette

Comment by kleopatra [ 07/Aug/12 ]

fixed by completely removing the trick: it was a c&p from core (while-not-believing-in-the-performance-effect-wanno-be-on-the-safe-side Now returns its opacity property as set, which definitely is the right thingy to do.

Still don't oversee all the implications, looking at some visual tests and the demos doesn't reveal anything obvious. On the other hand, we have to fix a blocker regarding opacity in JXPanel (Issue 1514) anyway, so could handle any upcoming side-effects in one go.

Committed as of revision #4222





Generated at Sat Jul 04 01:58:01 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.