FlatLaf icon indicating copy to clipboard operation
FlatLaf copied to clipboard

Switch widget

Open Poivin opened this issue 4 years ago • 9 comments

Hi,

Is there a way in flatLAF to give to checkbox the appearance of a switch widget ? It is useful when we need a binary widget out of True/false entries. Do you think it could be part of the look and feel? I don't think that Java proposes widget like that (Java 1.8 almost).

Poivin avatar Jul 29 '20 14:07 Poivin

Do you mean something like this? grafik

Yes, this is coming to FlatLaf, when I find some free time... Currently focusing on fixing last issues before releasing FlatLaf 1.0.

The plan is to implement such switches based on JToggleButton.

If you need it for JCheckBox now, you can do this easily by implementing a icon class (implements javax.swing.Icon) that paints the two states. Icon.paintIcon(Component, Graphics, int, int) receives the checkbox as first argument and you can use ((JCheckBox)c).isSelected() to decide what state to paint. Then use JCheckBox.setIcon( icon ) to assign the switch icon to a checkbox.

DevCharly avatar Jul 29 '20 15:07 DevCharly

Awesome, ok thanks Karl. I put CheckBox but if it is a toggleButton it is good as well! A question about color because your exemple illustrate for me a On/off feature or a activate/deactivate. but i think users could be also interested in a neutral binary widget without changing its color. For exemple for a switch of display 2D <==> 3D .

Keep up the good work as you do since beginning of this great LaF! And good luck for flatLaf 1.0

Poivin avatar Jul 29 '20 15:07 Poivin

Hello, All.

On one of our projects, taking FlatCheckBoxIcon as a basis, we use such an icon for our component.

ezgif-7-8794796bc94f

Set the icon to JCheckBox

Sample code

class SwitchIcon extends FlatCheckBoxIcon {
    private static final int ICON_WIDTH = 28;
    private static final int ICON_HEIGHT = 16;
    private final static int KNOB_WIDTH = ICON_WIDTH / 2 - 2;
    private final static int KNOB_HEIGHT = ICON_HEIGHT - 5;

    public SwitchIcon() {
        super();
    }

    protected void paintBorder(Graphics2D g2) {
        int arcwh = arc;
        g2.fillRoundRect(3, 0, ICON_WIDTH - 1, ICON_HEIGHT - 1, arcwh, arcwh);
    }

    protected void paintBackground(Graphics2D g2) {
        int arcwh = arc - 1;
        g2.fillRoundRect(4, 1, ICON_WIDTH - 3, ICON_HEIGHT - 3, arcwh, arcwh);
    }

    protected void paintCheckmark(Graphics2D g2) {
        int arcwh = arc - 1;
        g2.fillRoundRect(5, 2, KNOB_WIDTH, KNOB_HEIGHT, arcwh, arcwh);
    }

    @Override
    protected void paintIcon(Component c, Graphics2D g2) {
        boolean selected = (c instanceof AbstractButton && ((AbstractButton) c).isSelected());
        super.paintIcon(c, g2);
        if (!selected) {
            g2.setColor(disabledCheckmarkColor);
            int x = KNOB_WIDTH - 1;
            g2.translate(x, 0);
            paintCheckmark(g2);
            g2.translate(-(x), 0);
        }
    }

    @Override
    public int getIconWidth() {
        return ICON_WIDTH;
    }

    @Override
    public int getIconHeight() {
        return ICON_HEIGHT;
    }
}

koden8 avatar Jul 30 '20 08:07 koden8

@dinix2008 awesome, thanks for sharing 👍

But shouldn't the blue knob placed on the right side if selected? Usually knob is on the left if switch is off, and on the right if on.

DevCharly avatar Jul 30 '20 22:07 DevCharly

@Poivin wrote:

A question about color because your exemple illustrate for me a On/off feature or a activate/deactivate

The switches in the image are taken from Windows 10 just as a sample. I'm not gonna use this style in FlatLaf because it does not fit to the overall style.

DevCharly avatar Jul 30 '20 22:07 DevCharly

@DevCharly

But shouldn't the blue knob placed on the right side if selected? Usually knob is on the left if switch is off, and on the right if on.

On your advice, I corrected the position of the knob and set the radius fixedly equal to the width of the icon, it turned out like this: ezgif-6-3eb08a045b4c

class SwitchIcon extends FlatCheckBoxIcon {
    private static final int ICON_WIDTH = 28;
    private static final int ICON_HEIGHT = 16;
    private final static int KNOB_WIDTH = ICON_WIDTH / 2 - 2;
    private final static int KNOB_HEIGHT = ICON_HEIGHT - 5;
    protected final int arc = ICON_HEIGHT;

    public SwitchIcon() {
        super();
    }

    protected void paintBorder(Graphics2D g2) {
        int arcwh = arc;
        g2.fillRoundRect(3, 0, ICON_WIDTH - 1, ICON_HEIGHT - 1, arcwh, arcwh);
    }

    protected void paintBackground(Graphics2D g2) {
        int arcwh = arc - 1;
        g2.fillRoundRect(4, 1, ICON_WIDTH - 3, ICON_HEIGHT - 3, arcwh, arcwh);
    }

    protected void paintCheckmark(Graphics2D g2) {
        int arcwh = arc - 1;
        int x = KNOB_WIDTH - 1;
        g2.translate(x, 0);
        g2.fillRoundRect(5, 2, KNOB_WIDTH, KNOB_HEIGHT, arcwh, arcwh);
        g2.translate(-(x), 0);
    }

    @Override
    protected void paintIcon(Component c, Graphics2D g2) {
        boolean selected = (c instanceof AbstractButton && ((AbstractButton) c).isSelected());
        super.paintIcon(c, g2);
        if (!selected) {
            g2.setColor(disabledCheckmarkColor);
            int x = KNOB_WIDTH - 1;
            g2.translate(-x, 0);
            paintCheckmark(g2);
            g2.translate(+(x), 0);
        }
    }

    @Override
    public int getIconWidth() {
        return ICON_WIDTH;
    }

    @Override
    public int getIconHeight() {
        return ICON_HEIGHT;
    }
}

koden8 avatar Jul 31 '20 05:07 koden8

Thanks for this @dinix2008. I integrated it with my application, but I'm seeing the colour behind the left hand side of the switch after pressing it once.

This is Flat Light

It seems related to the colour of the theme as well. This is Monokai Pro Contrast

This is dark purple

grimlock81 avatar Jul 31 '20 06:07 grimlock81

@grimlock81 I've only tested on FlatLightLaf / FlatDarkLaf. Made the necessary fixes and now on FlatDarculaLaf / FlatIntelliJLaf it is also displayed corrected

Снимок экрана 2020-07-31 в 09 14 26 Снимок экрана 2020-07-31 в 09 14 41
class SwitchIcon extends FlatCheckBoxIcon {
    private static final int ICON_WIDTH = 28;
    private static final int ICON_HEIGHT = 16;
    private final static int KNOB_WIDTH = ICON_WIDTH / 2 - 2;
    private final static int KNOB_HEIGHT = ICON_HEIGHT - 5;
    protected final int arc = ICON_HEIGHT;

    public SwitchIcon() {
        super();
    }

    protected void paintBorder(Graphics2D g2) {
        int arcwh = arc;
        g2.fillRoundRect(3, 0, ICON_WIDTH - 1, ICON_HEIGHT - 1, arcwh, arcwh);
    }

    protected void paintBackground(Graphics2D g2) {
        int arcwh = arc - 1;
        g2.fillRoundRect(4, 1, ICON_WIDTH - 3, ICON_HEIGHT - 3, arcwh, arcwh);
    }

    protected void paintCheckmark(Graphics2D g2) {
        int arcwh = arc - 1;
        int x = KNOB_WIDTH - 1;
        g2.translate(x, 0);
        g2.fillRoundRect(5, 2, KNOB_WIDTH, KNOB_HEIGHT, arcwh, arcwh);
        g2.translate(-(x), 0);
    }

    @Override
    protected void paintFocusBorder(Graphics2D g2) {
        int w = getIconWidth() - 1 + (focusWidth * 2);
        int h = getIconHeight() - 1 + (focusWidth * 2);
        int arcwh = arc + (focusWidth * 2);
        g2.fillRoundRect(-focusWidth + 3, -focusWidth, w, h, arcwh, arcwh);
    }

    @Override
    protected void paintIndeterminate(Graphics2D g2) {
        super.paintIndeterminate(g2);
    }

    @Override
    protected void paintIcon(Component c, Graphics2D g2) {
        boolean selected = (c instanceof AbstractButton && ((AbstractButton) c).isSelected());
        super.paintIcon(c, g2);
        if (!selected) {
            g2.setColor(disabledCheckmarkColor);
            int x = KNOB_WIDTH - 1;
            g2.translate(-x, 0);
            paintCheckmark(g2);
            g2.translate(x, 0);
        }
    }

    @Override
    public int getIconWidth() {
        return ICON_WIDTH;
    }

    @Override
    public int getIconHeight() {
        return ICON_HEIGHT;
    }
}

koden8 avatar Jul 31 '20 06:07 koden8

Thanks again @dinix2008. I've tried it on a few themes and they all look perfect.

BTW, if anyone is interested, to make the switch respect the arc property "CheckBox.arc" remove this line protected final int arc = ICON_HEIGHT; and arc variable will be inherited from the superclass. But the depending on the value set it may not look as nice as the dinix2008's version.

grimlock81 avatar Jul 31 '20 06:07 grimlock81