JFormDesigner / FlatLaf

FlatLaf - Swing Look and Feel (with Darcula/IntelliJ themes support)
https://www.formdev.com/flatlaf/
Apache License 2.0
3.45k stars 274 forks source link

Switch widget #135

Open Poivin opened 4 years ago

Poivin commented 4 years ago

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).

DevCharly commented 4 years ago

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.

Poivin commented 4 years ago

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

koden8 commented 4 years ago

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;
    }
}
DevCharly commented 4 years ago

@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 commented 4 years ago

@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.

koden8 commented 4 years ago

@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;
    }
}
grimlock81 commented 4 years ago

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

koden8 commented 4 years ago

@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;
    }
}
grimlock81 commented 4 years ago

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.

GrobiDev commented 3 weeks ago

Hi @DevCharly, is there still the plan to integrate the toggle switch directly into FlatLaf? Either as a separate control, as a toggle button style or as a checkbox icon.

VISTALL commented 6 days ago

Hello. Will be nice to have it :) Waiting this style of button too