com.commons.color.CustomColorPicker.java Source code

Java tutorial

Introduction

Here is the source code for com.commons.color.CustomColorPicker.java

Source

/*
 * ******************************************************************************
 *  * Copyright 2015 See AUTHORS file.
 *  *
 *  * Licensed under the Apache License, Version 2.0 (the "License");
 *  * you may not use this file except in compliance with the License.
 *  * You may obtain a copy of the License at
 *  *
 *  *   http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS,
 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  * See the License for the specific language governing permissions and
 *  * limitations under the License.
 *  *****************************************************************************
 */

package com.commons.color;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.I18NBundle;
import com.kotcrab.vis.ui.Sizes;
import com.kotcrab.vis.ui.VisUI;
import com.kotcrab.vis.ui.util.ColorUtils;
import com.kotcrab.vis.ui.widget.*;
import com.kotcrab.vis.ui.widget.VisTextField.TextFieldFilter;
import com.kotcrab.vis.ui.widget.color.AlphaImage;
import com.kotcrab.vis.ui.widget.color.ColorPickerStyle;
import com.kotcrab.vis.ui.widget.color.Palette;
import com.kotcrab.vis.ui.widget.color.VerticalChannelBar;

import static com.commons.color.ColorPickerText.*;

/**
 * Created by azakhary on 7/14/2015.
 * some methods are modified to better fit the needs of real time updating.
 * Also using new ColorPickerListener that has method "changed"
 *
 *
 * @author Kotcrab
 */
public class CustomColorPicker extends VisWindow implements Disposable {

    private static final Drawable WHITE = VisUI.getSkin().getDrawable("white");

    static final int FIELD_WIDTH = 50;
    static final int HEX_FIELD_WIDTH = 95;

    static final int PALETTE_SIZE = 160;
    static final int BAR_WIDTH = 130;
    static final int BAR_HEIGHT = 11;
    static final float VERTICAL_BAR_WIDTH = 15;

    private ColorPickerStyle style;
    private Sizes sizes;
    private I18NBundle bundle;

    private ColorPickerListener listener;

    private Color oldColor;
    private Color color;
    private Color tmpColor; //temp color used for hsv to rgb calculations

    private Pixmap barPixmap;
    private Texture barTexture;
    private VerticalChannelBar verticalBar;

    private Texture paletteTexture;
    private Pixmap palettePixmap;
    private Palette palette;

    private ColorChannelWidget hBar;
    private ColorChannelWidget sBar;
    private ColorChannelWidget vBar;

    private ColorChannelWidget rBar;
    private ColorChannelWidget gBar;
    private ColorChannelWidget bBar;

    private ColorChannelWidget aBar;

    private VisValidatableTextField hexField;

    private VisTextButton restoreButton;
    private VisTextButton cancelButton;
    private VisTextButton okButton;

    private Image currentColor;
    private Image newColor;

    private boolean pickerCreated = false;

    private boolean closeAfterPickingFinished = true;

    public CustomColorPicker() {
        this((String) null);
    }

    public CustomColorPicker(String title) {
        this("default", title, null);
    }

    public CustomColorPicker(String title, ColorPickerListener listener) {
        this("default", title, listener);
    }

    public CustomColorPicker(ColorPickerListener listener) {
        this("default", null, listener);
    }

    public CustomColorPicker(String styleName, String title, ColorPickerListener listener) {
        super(title != null ? title : "");
        this.listener = listener;
        this.style = VisUI.getSkin().get(styleName, ColorPickerStyle.class);
        this.sizes = VisUI.getSizes();
        this.bundle = VisUI.getColorPickerBundle();

        if (title == null)
            getTitleLabel().setText(getText(TITLE));

        setModal(true);
        setMovable(true);

        addCloseButton();
        closeOnEscape();

        oldColor = new Color(Color.BLACK);
        color = new Color(Color.BLACK);
        tmpColor = new Color(Color.BLACK);

        createColorWidgets();
        createUI();
        createListeners();
        updatePixmaps();

        pack();
        centerWindow();

        setStyle(VisUI.getSkin().get("box", WindowStyle.class));
        getTitleLabel().setAlignment(Align.left);

        pickerCreated = true;
    }

    @Override
    public void addCloseButton() {
        VisImageButton closeButton = new VisImageButton("close-panel");
        this.getTitleTable().add(closeButton).padBottom(2);
        closeButton.addListener(new ChangeListener() {
            public void changed(ChangeEvent event, Actor actor) {
                CustomColorPicker.this.close();
            }
        });
        if (this.getTitleTable().getChildren().size == 2) {
            this.getTitleTable().getCell(this.getTitleLabel()).padLeft(closeButton.getWidth() * 2.0F);
        }
    }

    private void createUI() {
        VisTable rightTable = new VisTable(true);

        rightTable.add(hBar).row();
        rightTable.add(sBar).row();
        rightTable.add(vBar).row();

        rightTable.add();
        rightTable.row();

        rightTable.add(rBar).row();
        rightTable.add(gBar).row();
        rightTable.add(bBar).row();

        rightTable.add();
        rightTable.row();

        rightTable.add(aBar).row();

        VisTable leftTable = new VisTable(true);
        leftTable.add(palette).size(PALETTE_SIZE * sizes.scaleFactor);
        leftTable.row();
        leftTable.add(createColorsPreviewTable()).expandX().fillX();
        leftTable.row();
        leftTable.add(createHexTable()).expandX().left();

        add(leftTable).top().padRight(5);
        add(verticalBar).size(VERTICAL_BAR_WIDTH * sizes.scaleFactor, PALETTE_SIZE * sizes.scaleFactor).top();
        add(rightTable).expand().left().top().pad(4);
        row();
        add(createButtons()).pad(3).right().expandX().colspan(3);
    }

    private VisTable createColorsPreviewTable() {
        VisTable table = new VisTable(false);
        table.add(new VisLabel(getText(OLD))).spaceRight(3);
        table.add(currentColor = new AlphaImage(style)).height(25 * sizes.scaleFactor).expandX().fillX();
        table.row();
        table.add(new VisLabel(getText(NEW))).spaceRight(3);
        table.add(newColor = new AlphaImage(style, true)).height(25 * sizes.scaleFactor).expandX().fillX();

        currentColor.setColor(color);
        newColor.setColor(color);

        return table;
    }

    private VisTable createHexTable() {
        VisTable table = new VisTable(true);
        table.add(new VisLabel(getText(HEX)));
        table.add(hexField = new VisValidatableTextField("00000000")).width(HEX_FIELD_WIDTH * sizes.scaleFactor);
        table.row();

        hexField.setMaxLength(8);
        hexField.setProgrammaticChangeEvents(false);
        hexField.setTextFieldFilter(new TextFieldFilter() {
            @Override
            public boolean acceptChar(VisTextField textField, char c) {
                return Character.isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
            }
        });

        hexField.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                if (hexField.getText().length() == 8)
                    setColor(Color.valueOf(hexField.getText()), false);
            }
        });

        return table;
    }

    private VisTable createButtons() {
        VisTable table = new VisTable(true);
        table.defaults().right();
        table.add(restoreButton = new VisTextButton(getText(RESTORE)));
        table.add(okButton = new VisTextButton(getText(OK)));
        table.add(cancelButton = new VisTextButton(getText(CANCEL)));
        return table;
    }

    private void createColorWidgets() {
        palettePixmap = new Pixmap(100, 100, Format.RGB888);
        paletteTexture = new Texture(palettePixmap);

        barPixmap = new Pixmap(1, 360, Format.RGB888);

        for (int h = 0; h < 360; h++) {
            ColorUtils.HSVtoRGB(360 - h, 100, 100, tmpColor);
            barPixmap.drawPixel(0, h, Color.rgba8888(tmpColor));
        }

        barTexture = new Texture(barPixmap);

        palette = new Palette(style, sizes, paletteTexture, 0, 0, 100, new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                sBar.setValue(palette.getV());
                vBar.setValue(palette.getS());

                updateHSVValuesFromFields();
                updatePixmaps();
            }
        });

        verticalBar = new VerticalChannelBar(style, sizes, barTexture, 0, 360, new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                hBar.setValue(verticalBar.getValue());
                updateHSVValuesFromFields();
                updatePixmaps();
            }
        });

        hBar = new ColorChannelWidget(style, sizes, "H", 360, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                verticalBar.setValue(hBar.getValue());
                updateHSVValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int h = 0; h < 360; h++) {
                    ColorUtils.HSVtoRGB(h, sBar.getValue(), vBar.getValue(), tmpColor);
                    pixmap.drawPixel(h, 0, Color.rgba8888(tmpColor));
                }
            }
        });

        sBar = new ColorChannelWidget(style, sizes, "S", 100, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                palette.setValue(vBar.getValue(), sBar.getValue());
                updateHSVValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int s = 0; s < 100; s++) {
                    ColorUtils.HSVtoRGB(hBar.getValue(), s, vBar.getValue(), tmpColor);
                    pixmap.drawPixel(s, 0, Color.rgba8888(tmpColor));
                }
            }
        });

        vBar = new ColorChannelWidget(style, sizes, "V", 100, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                palette.setValue(vBar.getValue(), sBar.getValue());
                updateHSVValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int v = 0; v < 100; v++) {
                    ColorUtils.HSVtoRGB(hBar.getValue(), sBar.getValue(), v, tmpColor);
                    pixmap.drawPixel(v, 0, Color.rgba8888(tmpColor));
                }

            }
        });

        rBar = new ColorChannelWidget(style, sizes, "R", 255, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                updateRGBValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int r = 0; r < 255; r++) {
                    tmpColor.set(r / 255.0f, color.g, color.b, 1);
                    pixmap.drawPixel(r, 0, Color.rgba8888(tmpColor));
                }
            }
        });

        gBar = new ColorChannelWidget(style, sizes, "G", 255, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                updateRGBValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int g = 0; g < 255; g++) {
                    tmpColor.set(color.r, g / 255.0f, color.b, 1);
                    pixmap.drawPixel(g, 0, Color.rgba8888(tmpColor));
                }
            }
        });

        bBar = new ColorChannelWidget(style, sizes, "B", 255, new ColorChannelWidget.ColorChannelWidgetListener() {
            @Override
            public void updateFields() {
                updateRGBValuesFromFields();
                updatePixmaps();
            }

            @Override
            public void draw(Pixmap pixmap) {
                for (int b = 0; b < 255; b++) {
                    tmpColor.set(color.r, color.g, b / 255.0f, 1);
                    pixmap.drawPixel(b, 0, Color.rgba8888(tmpColor));
                }

            }
        });

        aBar = new ColorChannelWidget(style, sizes, "A", 255, true,
                new ColorChannelWidget.ColorChannelWidgetListener() {
                    @Override
                    public void updateFields() {
                        if (aBar.isInputValid())
                            color.a = aBar.getValue() / 255.0f;
                        updatePixmaps();
                    }

                    @Override
                    public void draw(Pixmap pixmap) {
                        pixmap.fill();
                        for (int i = 0; i < 255; i++) {
                            tmpColor.set(color.r, color.g, color.b, i / 255.0f);
                            pixmap.drawPixel(i, 0, Color.rgba8888(tmpColor));
                        }
                    }
                });
    }

    private void createListeners() {
        restoreButton.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                setColor(oldColor);
            }
        });

        okButton.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                if (listener != null)
                    listener.finished(new Color(color));
                setColor(color);
                if (closeAfterPickingFinished)
                    fadeOut();
            }
        });

        cancelButton.addListener(new ChangeListener() {
            @Override
            public void changed(ChangeEvent event, Actor actor) {
                setColor(oldColor);
                close();
            }
        });
    }

    @Override
    protected void close() {
        if (listener != null)
            listener.canceled();
        super.close();
    }

    public ColorPickerListener getListener() {
        return listener;
    }

    public void setListener(ColorPickerListener listener) {
        this.listener = listener;
    }

    private void updatePixmaps() {
        for (int v = 0; v <= 100; v++) {
            for (int s = 0; s <= 100; s++) {
                ColorUtils.HSVtoRGB(hBar.getValue(), s, v, tmpColor);
                palettePixmap.drawPixel(v, 100 - s, Color.rgba8888(tmpColor));
            }
        }

        paletteTexture.draw(palettePixmap, 0, 0);

        newColor.setColor(color);

        hBar.redraw();
        sBar.redraw();
        vBar.redraw();

        rBar.redraw();
        gBar.redraw();
        bBar.redraw();

        aBar.redraw();

        hexField.setText(color.toString().toUpperCase());
        hexField.setCursorPosition(hexField.getMaxLength());

        if (listener != null && pickerCreated)
            listener.changed(new Color(color));
    }

    @Override
    /** Sets current selected color in picker.*/
    public void setColor(Color c) {
        //this method overrides setColor in Actor, not big deal we definitely don't need it
        setColor(c, true);
    }

    private void setColor(Color c, boolean updateCurrentColor) {
        if (updateCurrentColor) {
            currentColor.setColor(new Color(c));
            oldColor = new Color(c);
        }
        color = new Color(c);
        updateFieldsFromColor();
        updatePixmaps();
    }

    private String getText(ColorPickerText text) {
        return bundle.get(text.getName());
    }

    /**
     * Controls whether to fade out color picker after users finished color picking and has pressed OK button. If
     * this is set to false picker won't close after pressing OK button. Default is true.
     * Note that  by default picker is a modal window so might also want to call {@code colorPicker.setModal(false)} to
     * disable it.
     */
    public void setCloseAfterPickingFinished(boolean closeAfterPickingFinished) {
        this.closeAfterPickingFinished = closeAfterPickingFinished;
    }

    @Override
    public void dispose() {
        paletteTexture.dispose();
        barTexture.dispose();

        palettePixmap.dispose();
        barPixmap.dispose();

        hBar.dispose();
        sBar.dispose();
        vBar.dispose();

        rBar.dispose();
        gBar.dispose();
        bBar.dispose();

        aBar.dispose();
    }

    private void updateFieldsFromColor() {
        int[] hsv = ColorUtils.RGBtoHSV(color);
        int ch = hsv[0];
        int cs = hsv[1];
        int cv = hsv[2];

        int cr = MathUtils.round(color.r * 255.0f);
        int cg = MathUtils.round(color.g * 255.0f);
        int cb = MathUtils.round(color.b * 255.0f);
        int ca = MathUtils.round(color.a * 255.0f);

        hBar.setValue(ch);
        sBar.setValue(cs);
        vBar.setValue(cv);

        rBar.setValue(cr);
        gBar.setValue(cg);
        bBar.setValue(cb);

        aBar.setValue(ca);

        verticalBar.setValue(hBar.getValue());
        palette.setValue(vBar.getValue(), sBar.getValue());
    }

    private void updateHSVValuesFromFields() {
        int[] hsv = ColorUtils.RGBtoHSV(color);
        int h = hsv[0];
        int s = hsv[1];
        int v = hsv[2];

        if (hBar.isInputValid())
            h = hBar.getValue();
        if (sBar.isInputValid())
            s = sBar.getValue();
        if (vBar.isInputValid())
            v = vBar.getValue();

        color = ColorUtils.HSVtoRGB(h, s, v, color.a);

        int cr = MathUtils.round(color.r * 255.0f);
        int cg = MathUtils.round(color.g * 255.0f);
        int cb = MathUtils.round(color.b * 255.0f);

        rBar.setValue(cr);
        gBar.setValue(cg);
        bBar.setValue(cb);
    }

    private void updateRGBValuesFromFields() {
        int r = MathUtils.round(color.r * 255.0f);
        int g = MathUtils.round(color.g * 255.0f);
        int b = MathUtils.round(color.b * 255.0f);

        if (rBar.isInputValid())
            r = rBar.getValue();
        if (gBar.isInputValid())
            g = gBar.getValue();
        if (bBar.isInputValid())
            b = bBar.getValue();

        color.set(r / 255.0f, g / 255.0f, b / 255.0f, color.a);

        int[] hsv = ColorUtils.RGBtoHSV(color);
        int ch = hsv[0];
        int cs = hsv[1];
        int cv = hsv[2];

        hBar.setValue(ch);
        sBar.setValue(cs);
        vBar.setValue(cv);

        verticalBar.setValue(hBar.getValue());
        palette.setValue(vBar.getValue(), sBar.getValue());
    }
}