mgarin / weblaf

WebLaF is a fully open-source Look & Feel and component library written in pure Java for cross-platform desktop Swing applications.
http://weblookandfeel.com
GNU General Public License v3.0
1.14k stars 235 forks source link

WebComboBox popup width #588

Closed husker-dev closed 4 years ago

husker-dev commented 4 years ago

I want to set popup width automaticly by its largest content. I have tried solutions from internet but they don't work with WebLaF. I think it's because of custom popup. Is there a way to do it in WebLaF?

image

mgarin commented 4 years ago

Currently WebComboBox has a separate option for that:

    /**
     * Returns whether or not wide popup is allowed.
     *
     * @return {@code true} if wide popup is allowed, {@code false} otherwise
     */
    public boolean isWidePopup ()
    {
        return getUI ().isWidePopup ();
    }

    /**
     * Sets whether or not wide popup is allowed.
     *
     * @param wide whether or not wide popup is allowed
     */
    public void setWidePopup ( final boolean wide )
    {
        getUI ().setWidePopup ( wide );
    }

Although if you can't use the custom component - you can workaround it like this:

final JComboBox comboBox = new JComboBox ();
if ( comboBox.getUI () instanceof WComboBoxUI )
{
    ( ( WComboBoxUI ) comboBox.getUI () ).setWidePopup ( true );
}

I will probably change the way this custom option is set to use client properties instead, so you will be able to use approach similar to solution I've made for custom JTabbedPane settings (explained in this comment).

Sciss commented 4 years ago

@Husker-hub For this situation, there is API: JComboBox#setPrototypeDisplayValue. From the API docs:

Sets the prototype display value used to calculate the size of the display for the UI portion.

If a prototype display value is specified, the preferred size of the combo box is calculated by configuring the renderer with the prototype display value and obtaining its preferred size. Specifying the preferred display value is often useful when the combo box will be displaying large amounts of data. If no prototype display value has been specified, the renderer must be configured for each value from the model and its preferred size obtained, which can be relatively expensive.

So I think the solution is to set the prototype value to the longest string representation of your model.

mgarin commented 4 years ago

@Sciss

So I think the solution is to set the prototype value to the longest string representation of your model.

That will only help if you have space for combobox to take it's preferred size - pretty sure in the case on the screenshot combobox is in one of GridLayout cells and is forced to shrink down.

Also prototype value is not specified by default which forces combobox to use all values to calculate it's preferred size (render them all with -1 index to see which has largest size), so it is most probably already has a proper preferred size but it is irrelevant when size is dictated by the layout.

Also WebLaF forces popup to have the same size as combobox itself by default, even if the list renderers do not fit in (for instance because combobox was shrunk or for example because list renderer has some weird chunky styling with extra padding and whatnot).

husker-dev commented 4 years ago

I forgot to write that I have already tried to use setWidePopup(true); and nothing has chenged. Can it not work due to the fact that I dynamically change the elements?

mgarin commented 4 years ago

That could be a reason, but I'm not sure. Can you provide a small example with the dynamic elements change that doesn't work (go wide) for you?

husker-dev commented 4 years ago

Here is my example:

WebComboBox constants = new WebComboBox(){{
    setPreferredWidth(20);
    addItem("Custom");

    setWidePopup(true);

    Constants.addListener(() -> {
        removeAllItems();
        addItem("Custom");
        for(String tag : Constants.getConstants(constType))
            addItem(tag);
    });
}};
mgarin commented 4 years ago

Ok, now I see why I wasn't able to reproduce it :)

Here is the code that adjusts popup size for "wide" ones:

if ( isWidePopup () )
{
    final Dimension prefSize = comboBox.getPreferredSize ();
    if ( prefSize.width > comboSize.width )
    {
        comboSize.width = prefSize.width;
    }
}

And you have combobox width (which is in prefSize in this code piece) hard set to 20, so it obviously never gets to the part of adjusting popup size because it still thinks that it's big enough to fit all elements.

I guess that was a cheap solution to adjust popup size without any extra effort. And it is incorrect in either case actually because combobox preferred size might be smaller than popup preferred size with the same items - it all depends on the styling.

Thanks for pointing out this issue, i'll add a fix for this in v1.2.11 which should be released on next Monday, so not too far away.

mgarin commented 4 years ago

To add to the pile: I found a few other issues with the popup size calculations, I'll fix those as well along the way. That part of code is really dated even though I was pretty sure I've adjusted it some time ago to avoid any issues with the new styling implementation.

mgarin commented 4 years ago

I've added a bunch of fixes and improvements for the combobox popup positioning and size calculations - it should now properly work even with combobox having set preferred size.

Here is an example: image

And if "wide popup" option is disabled: image

Combobox preferred width is set to 20px in this example. Windows native decoration doesn't let it shrink to less than ~140px, so it's still quite wide but still not wide enough for content.

Also I've changed how "wide popup" property can be set, it now uses new and more convenient client property object to store and retrieve the setting. That allows you to use it with Swing JComboBox as well if needed:

final JComboBox comboBox = new JComboBox ();
WebComboBox.WIDE_POPUP.set ( comboBox, true );

But WebComboBox also still has public methods to change that setting:

final WebComboBox comboBox = new WebComboBox ();
comboBox.setWidePopup ( true );

These changes will be available in v1.2.11 update which should be out either today or tomorrow. You can also test these changes on SNAPSHOT versions which will be available in a few minutes.

mgarin commented 4 years ago

I've added a small fix for issue that slipped from me because I was debugging on a long list: https://github.com/mgarin/weblaf/commit/ddf66b5db398f12f7a3647a4bbd9e640130beeeb Now it should work properly :)