/* * ****************************************************************************** * * 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()); } }