/******************************************************************************* * MIT License * * Copyright (c) 2018 Raymond Buckley * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. ******************************************************************************/ package com.ray3k.skincomposer.dialog; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Input.Keys; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.*; import com.badlogic.gdx.scenes.scene2d.ui.*; 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.Array; import com.badlogic.gdx.utils.Sort; import com.ray3k.skincomposer.Main; import com.ray3k.skincomposer.UndoableManager; import com.ray3k.skincomposer.UndoableManager.ColorUndoable; import com.ray3k.skincomposer.UndoableManager.CustomColorUndoable; import com.ray3k.skincomposer.data.*; import com.ray3k.skincomposer.utils.Utils; import java.util.Comparator; public class DialogColors extends Dialog { private Array<ColorData> colors; private Table colorTable; private StyleProperty styleProperty; private CustomProperty customProperty; private boolean selectingForTintedDrawable; private SelectBox<String> selectBox; private DialogColorsListener listener; private ScrollPane scrollPane; private Main main; public DialogColors(Main main, StyleProperty styleProperty, boolean selectingForTintedDrawable, DialogColorsListener listener) { super("", main.getSkin(), "dialog"); this.styleProperty = styleProperty; populate(main, selectingForTintedDrawable, listener); } public DialogColors(Main main, StyleProperty styleProperty, DialogColorsListener listener) { this(main, styleProperty, false, listener); } public DialogColors(Main main, CustomProperty customProperty, boolean selectingForTintedDrawable, DialogColorsListener listener) { super("", main.getSkin(), "dialog"); this.customProperty = customProperty; populate(main, selectingForTintedDrawable, listener); } public DialogColors(Main main, CustomProperty customProperty, DialogColorsListener listener) { this(main, customProperty, false, listener); } private void populate(Main main, boolean selectingForTintedDrawable, DialogColorsListener listener) { this.main = main; this.listener = listener; this.selectingForTintedDrawable = selectingForTintedDrawable; colors = main.getJsonData().getColors(); getContentTable().defaults().expandX(); if (styleProperty != null || customProperty != null) { Label label = new Label("Select a color...", getSkin(), "title"); label.setAlignment(Align.center); getContentTable().add(label); getContentTable().row(); } else if (selectingForTintedDrawable) { Label label = new Label("Select a color for tinted drawable...", getSkin(), "title"); label.setAlignment(Align.center); getContentTable().add(label); getContentTable().row(); } else { Label label = new Label("Colors", getSkin(), "title"); label.setAlignment(Align.center); getContentTable().add(label); getContentTable().row(); } Table table = new Table(); table.defaults().pad(2.0f); table.add(new Label("Sort by: ", getSkin())).padLeft(20.0f); selectBox = new SelectBox<String>(getSkin()); selectBox.setItems(new String[] {"A-Z", "Z-A"}); selectBox.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { sortBySelectedMode(); } }); selectBox.addListener(main.getHandListener()); selectBox.getList().addListener(main.getHandListener()); table.add(selectBox); TextButton imageButton = new TextButton("New Color", getSkin(), "new"); imageButton.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { showColorPicker(); } }); imageButton.addListener(main.getHandListener()); table.add(imageButton).expandX().left(); getContentTable().add(table).left().expandX(); getContentTable().row(); colorTable = new Table(); refreshTable(); table = new Table(); table.add(colorTable).pad(5.0f); scrollPane = new ScrollPane(table, getSkin()); scrollPane.setFadeScrollBars(false); getContentTable().add(scrollPane).grow(); if (styleProperty != null || customProperty != null) { button("Clear Color", true); button("Cancel", false); getButtonTable().getCells().get(0).getActor().addListener(main.getHandListener()); getButtonTable().getCells().get(1).getActor().addListener(main.getHandListener()); } else if (selectingForTintedDrawable) { button("Cancel", false); getButtonTable().getCells().get(0).getActor().addListener(main.getHandListener()); } else { button("Close", false); getButtonTable().getCells().get(0).getActor().addListener(main.getHandListener()); } getButtonTable().padBottom(15.0f); key(Keys.ESCAPE, false); } @Override public Dialog show(Stage stage) { Dialog dialog = super.show(stage); stage.setScrollFocus(scrollPane); return dialog; } private void showColorPicker() { main.getDialogFactory().showDialogColorPicker(new DialogColorPicker.ColorListener() { @Override public void selected(Color color) { if (color != null) { final TextField field = new TextField("RGBA_" + (int) (color.r * 255) + "_" + (int) (color.g * 255) + "_" + (int) (color.b * 255) + "_" + (int) (color.a * 255), getSkin()); final Dialog dialog = new Dialog("Color name...", getSkin(), "bg") { @Override protected void result(Object object) { if ((Boolean) object == true) { newColor(field.getText(), color); } } }; dialog.getTitleTable().padLeft(5.0f); dialog.button("Ok", true).button("Cancel", false).key(Keys.ESCAPE, false); final TextButton button = (TextButton) dialog.getButtonTable().getCells().first().getActor(); button.addListener(main.getHandListener()); dialog.getButtonTable().getCells().get(1).getActor().addListener(main.getHandListener()); dialog.getButtonTable().pad(15.0f); field.setTextFieldListener(new TextField.TextFieldListener() { @Override public void keyTyped(TextField textField, char c) { if (c == '\n') { if (!button.isDisabled()) { String name = field.getText(); if (newColor(name, color)) { dialog.hide(); } } main.getStage().setKeyboardFocus(textField); } } }); field.addListener(main.getIbeamListener()); dialog.getContentTable().padLeft(10.0f).padRight(10.0f).padTop(5.0f); dialog.text("Please enter a name for the new color: "); dialog.getContentTable().row(); dialog.getContentTable().add(field).growX(); dialog.getContentTable().row(); dialog.text("Preview:"); dialog.getContentTable().row(); Table table = new Table(getSkin()); table.setBackground("white"); table.setColor(color); dialog.getContentTable().add(table).minSize(50.0f); button.setDisabled(!ColorData.validate(field.getText())); field.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { boolean disable = !ColorData.validate(field.getText()); if (!disable) { for (ColorData data : main.getJsonData().getColors()) { if (data.getName().equals(field.getText())) { disable = true; break; } } } button.setDisabled(disable); } }); dialog.show(getStage()); getStage().setKeyboardFocus(field); field.selectAll(); field.setFocusTraversal(false); } } }); } public void refreshTable() { colorTable.clear(); if (colors.size > 0) { colorTable.defaults().padTop(5.0f); for (ColorData color : colors) { Button button = new Button(getSkin(), "color-base"); button.addListener(main.getHandListener()); Label label = new Label(color.toString(), getSkin(), "white"); label.setTouchable(Touchable.disabled); float brightness = Utils.brightness(color.color); Color borderColor; if (brightness > .35f) { borderColor = Color.BLACK; label.setColor(borderColor); } else { borderColor = Color.WHITE; label.setColor(borderColor); } Color bgColor = new Color(color.color.r, color.color.g, color.color.b, 1.0f); Table table = new Table(getSkin()); table.setBackground("white"); table.setColor(bgColor); table.add(label).pad(3.0f); if (styleProperty == null && customProperty == null && !selectingForTintedDrawable) { table.addCaptureListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { event.setBubbles(false); refreshTable(); } }); table.addCaptureListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { event.setBubbles(false); return true; } }); } Table borderTable = new Table(getSkin()); borderTable.setBackground("white"); borderTable.setColor(borderColor); borderTable.add(table).growX().pad(1.0f); button.add(borderTable).growX(); //rename button Button renameButton = new Button(getSkin(), "settings-small"); renameButton.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { renameDialog(color); event.setBubbles(false); } }); renameButton.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { event.setBubbles(false); return true; } }); button.add(renameButton).padLeft(10.0f); TextTooltip toolTip = new TextTooltip("Rename Color", main.getTooltipManager(), getSkin()); renameButton.addListener(toolTip); //recolor button Button recolorButton = new Button(getSkin(), "colorwheel"); recolorButton.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { recolorDialog(color); event.setBubbles(false); } }); recolorButton.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { event.setBubbles(false); return true; } }); button.add(recolorButton); toolTip = new TextTooltip("Change Color", main.getTooltipManager(), getSkin()); recolorButton.addListener(toolTip); label = new Label("(" + ((int)(color.color.r * 255)) + ", " + ((int)(color.color.g * 255)) + ", " + ((int)(color.color.b * 255)) + ", " + ((int)(color.color.a * 255)) + ")", getSkin()); label.setTouchable(Touchable.disabled); label.setAlignment(Align.center); if (styleProperty == null && customProperty == null && !selectingForTintedDrawable) { label.addCaptureListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { event.setBubbles(false); refreshTable(); } }); label.addCaptureListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { event.setBubbles(false); return true; } }); } button.add(label).padLeft(5.0f).minWidth(160.0f); //delete color button Button closeButton = new Button(getSkin(), "delete-small"); final ColorData deleteColor = color; closeButton.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { colors.removeValue(deleteColor, true); main.getProjectData().setChangesSaved(false); //clear style properties that use this color. for (Array<StyleData> datas : main.getJsonData().getClassStyleMap().values()) { for (StyleData data : datas) { for (StyleProperty property : data.properties.values()) { if (property != null && property.type.equals(Color.class) && property.value != null && property.value.equals(deleteColor.getName())) { property.value = null; } } } } //delete tinted drawables based on this color. for(DrawableData drawableData : new Array<>(main.getProjectData().getAtlasData().getDrawables())) { if (drawableData.tintName != null && drawableData.tintName.equals(deleteColor.getName())) { main.getProjectData().getAtlasData().getDrawables().removeValue(drawableData, true); //clear any style properties based on this tinted drawable. for (Array<StyleData> styleDatas : main.getJsonData().getClassStyleMap().values()) { for (StyleData styleData : styleDatas) { for (StyleProperty styleProperty : styleData.properties.values()) { if (styleProperty != null && styleProperty.type.equals(Drawable.class) && styleProperty.value != null && styleProperty.value.equals(drawableData.toString())) { styleProperty.value = null; } } } } } } main.getUndoableManager().clearUndoables(); main.getRootTable().refreshStyleProperties(true); main.getRootTable().refreshPreview(); event.setBubbles(false); refreshTable(); } }); closeButton.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { event.setBubbles(false); return true; } }); toolTip = new TextTooltip("Delete Color", main.getTooltipManager(), getSkin()); closeButton.addListener(toolTip); button.add(closeButton).padLeft(5.0f); if (styleProperty == null && customProperty == null && !selectingForTintedDrawable) { button.setTouchable(Touchable.childrenOnly); } else { setObject(button, color); final ColorData result = color; button.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { result(result); hide(); } }); } colorTable.add(button).growX(); colorTable.row(); } } else { colorTable.add(new Label("No colors have been set!", getSkin(), "black")); } } private void recolorDialog(ColorData colorData) { main.getDialogFactory().showDialogColorPicker(colorData.color, new DialogColorPicker.ColorListener() { @Override public void selected(Color color) { if (color != null) { recolorColor(colorData, color); } } }); } private void recolorColor(ColorData colorData, Color color) { colorData.color = color; main.getUndoableManager().clearUndoables(); main.getRootTable().refreshStyleProperties(true); main.getAtlasData().produceAtlas(); main.getRootTable().refreshPreview(); main.getProjectData().setChangesSaved(false); refreshTable(); } private void renameDialog(ColorData color) { TextField textField = new TextField("", getSkin()); TextButton okButton; Dialog dialog = new Dialog("Rename Color?", getSkin(), "bg") { @Override protected void result(Object object) { if ((boolean) object) { renameColor(color, textField.getText()); } } @Override public Dialog show(Stage stage) { Dialog dialog = super.show(stage); main.getStage().setKeyboardFocus(textField); return dialog; } }; dialog.getTitleTable().padLeft(5.0f); float brightness = Utils.brightness(color.color); Color borderColor; if (brightness > .35f) { borderColor = Color.BLACK; } else { borderColor = Color.WHITE; } Table bg = new Table(getSkin()); bg.setBackground("white"); bg.setColor(borderColor); dialog.getContentTable().add(bg); Label label = new Label(color.getName(), getSkin(), "white"); label.setColor(color.color); bg.add(label).pad(10); dialog.getContentTable().row(); label = new Label("What do you want to rename the color to?", getSkin()); dialog.getContentTable().add(label); dialog.getContentTable().row(); textField.setText(color.getName()); textField.selectAll(); textField.addListener(main.getIbeamListener()); dialog.getContentTable().add(textField); dialog.getCell(dialog.getContentTable()).pad(15.0f); dialog.button("OK", true); dialog.button("Cancel", false).key(Keys.ESCAPE, false); okButton = (TextButton) dialog.getButtonTable().getCells().first().getActor(); okButton.setDisabled(true); okButton.addListener(main.getHandListener()); dialog.getButtonTable().getCells().get(1).getActor().addListener(main.getHandListener()); dialog.getButtonTable().padBottom(15.0f); textField.addListener(new ChangeListener() { @Override public void changed(ChangeListener.ChangeEvent event, Actor actor) { boolean disable = !ColorData.validate(textField.getText()); if (!disable) { for (ColorData data : main.getJsonData().getColors()) { if (data.getName().equals(textField.getText())) { disable = true; break; } } } okButton.setDisabled(disable); } }); textField.setTextFieldListener(new TextField.TextFieldListener() { @Override public void keyTyped(TextField textField, char c) { if (c == '\n') { if (!okButton.isDisabled()) { renameColor(color, textField.getText()); dialog.hide(); } } } }); textField.setFocusTraversal(false); dialog.show(getStage()); } private void renameColor(ColorData color, String newName) { //style properties for (Array<StyleData> datas : main.getJsonData().getClassStyleMap().values()) { for (StyleData data : datas) { for (StyleProperty property : data.properties.values()) { if (property != null && property.type.equals(Color.class) && property.value != null && property.value.equals(color.getName())) { property.value = newName; } } } } for (DrawableData drawableData : main.getAtlasData().getDrawables()) { //tinted drawables if (drawableData.tintName != null && drawableData.tintName.equals(color.getName())) { drawableData.tintName = newName; } //ten patch drawables else if (drawableData.tenPatchData != null && drawableData.tenPatchData.colorName.equals(color.getName())) { drawableData.tenPatchData.colorName = newName; } } try { color.setName(newName); } catch (ColorData.NameFormatException ex) { Gdx.app.error(getClass().getName(), "Error trying to rename a color.", ex); main.getDialogFactory().showDialogError("Name Error...","Error while naming a color.\\nPlease ensure name is formatted appropriately:\\nNo spaces, don't start with a number, - and _ acceptable.\n\nOpen log?"); } main.getUndoableManager().clearUndoables(); main.getRootTable().refreshStyleProperties(true); main.getRootTable().refreshPreview(); main.getProjectData().setChangesSaved(false); refreshTable(); } private boolean newColor(String name, Color color) { if (ColorData.validate(name)) { try { main.getProjectData().setChangesSaved(false); colors.add(new ColorData(name, color)); sortBySelectedMode(); refreshTable(); return true; } catch (Exception e) { Gdx.app.log(getClass().getName(), "Error trying to add color.", e); main.getDialogFactory().showDialogError("Error creating color...", "Error while attempting to create color.\n\nOpen log?"); return false; } } else { return false; } } @Override protected void result(Object object) { boolean pressedCancel = false; if (styleProperty != null) { if (object instanceof ColorData) { main.getProjectData().setChangesSaved(false); ColorData color = (ColorData) object; ColorUndoable undoable = new ColorUndoable(main.getRootTable(), main.getJsonData(), styleProperty, styleProperty.value, color.getName()); main.getUndoableManager().addUndoable(undoable, true); } else if (object instanceof Boolean) { if ((boolean) object) { main.getProjectData().setChangesSaved(false); ColorUndoable undoable = new ColorUndoable(main.getRootTable(), main.getJsonData(), styleProperty, styleProperty.value, null); main.getUndoableManager().addUndoable(undoable, true); } else { pressedCancel = true; boolean hasColor = false; for (ColorData color : main.getJsonData().getColors()) { if (color.getName().equals(styleProperty.value)) { hasColor = true; break; } } if (!hasColor) { main.getProjectData().setChangesSaved(false); styleProperty.value = null; main.getRootTable().refreshStyleProperties(true); } } } } else if (customProperty != null) { if (object instanceof ColorData) { main.getProjectData().setChangesSaved(false); ColorData color = (ColorData) object; CustomColorUndoable undoable = new UndoableManager.CustomColorUndoable(main, customProperty, color.getName()); main.getUndoableManager().addUndoable(undoable, true); } else if (object instanceof Boolean) { if ((boolean) object) { main.getProjectData().setChangesSaved(false); CustomColorUndoable undoable = new UndoableManager.CustomColorUndoable(main, customProperty, null); main.getUndoableManager().addUndoable(undoable, true); main.getRootTable().refreshStyleProperties(true); } else { pressedCancel = true; boolean hasColor = false; for (ColorData color : main.getJsonData().getColors()) { if (color.getName().equals(customProperty.getValue())) { hasColor = true; break; } } if (!hasColor) { main.getProjectData().setChangesSaved(false); customProperty.setValue(null); main.getRootTable().refreshStyleProperties(true); } } } } if (listener != null) { if (object instanceof ColorData) { listener.handle((ColorData) object, pressedCancel); } else { listener.handle(null, pressedCancel); } } } private void sortBySelectedMode() { switch (selectBox.getSelectedIndex()) { case 0: sortFontsAZ(); break; case 1: sortFontsZA(); break; } } private void sortFontsAZ() { Sort.instance().sort(colors, new Comparator<ColorData>() { @Override public int compare(ColorData o1, ColorData o2) { return o1.toString().compareToIgnoreCase(o2.toString()); } }); refreshTable(); } private void sortFontsZA() { Sort.instance().sort(colors, new Comparator<ColorData>() { @Override public int compare(ColorData o1, ColorData o2) { return o1.toString().compareToIgnoreCase(o2.toString()) * -1; } }); refreshTable(); } public static interface DialogColorsListener { public void handle(ColorData colorData, boolean cancelled); } @Override public Dialog show(Stage stage, Action action) { fire(new DialogEvent(DialogEvent.Type.OPEN)); return super.show(stage, action); } @Override public boolean remove() { fire(new DialogEvent(DialogEvent.Type.CLOSE)); return super.remove(); } }