1/*******************************************************************************
2 * Copyright 2011 See AUTHORS file.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 ******************************************************************************/
16
17package com.badlogic.gdx.tools.flame;
18import java.awt.Color;
19import java.awt.Dimension;
20import java.awt.GradientPaint;
21import java.awt.Graphics;
22import java.awt.Graphics2D;
23import java.awt.GridBagConstraints;
24import java.awt.GridBagLayout;
25import java.awt.Insets;
26import java.awt.event.MouseAdapter;
27import java.awt.event.MouseEvent;
28import java.awt.event.MouseMotionAdapter;
29import java.util.ArrayList;
30
31import javax.swing.BorderFactory;
32import javax.swing.JColorChooser;
33import javax.swing.JPanel;
34import javax.swing.JSlider;
35import javax.swing.event.ChangeEvent;
36import javax.swing.event.ChangeListener;
37
38import com.badlogic.gdx.graphics.g3d.particles.values.GradientColorValue;
39
40/** @author Inferno */
41class GradientPanel extends ParticleValuePanel<GradientColorValue> {
42	private GradientEditor gradientEditor;
43	ColorSlider saturationSlider, lightnessSlider;
44	JPanel colorPanel;
45	private ColorSlider hueSlider;
46
47	public GradientPanel (FlameMain editor, GradientColorValue value, String name, String description,
48																boolean hideGradientEditor) {
49		super(editor, name, description);
50		setValue(value);
51
52		if (hideGradientEditor) {
53			gradientEditor.setVisible(false);
54		}
55		gradientEditor.percentages.clear();
56		for (float percent : value.getTimeline())
57			gradientEditor.percentages.add(percent);
58
59		gradientEditor.colors.clear();
60		float[] colors = value.getColors();
61		for (int i = 0; i < colors.length;) {
62			float r = colors[i++];
63			float g = colors[i++];
64			float b = colors[i++];
65			gradientEditor.colors.add(new Color(r, g, b));
66		}
67		if (gradientEditor.colors.isEmpty() || gradientEditor.percentages.isEmpty()) {
68			gradientEditor.percentages.clear();
69			gradientEditor.percentages.add(0f);
70			gradientEditor.percentages.add(1f);
71			gradientEditor.colors.clear();
72			gradientEditor.colors.add(Color.white);
73		}
74		setColor(gradientEditor.colors.get(0));
75	}
76
77	public Dimension getPreferredSize () {
78		Dimension size = super.getPreferredSize();
79		size.width = 10;
80		return size;
81	}
82
83	protected void initializeComponents () {
84		super.initializeComponents();
85		JPanel contentPanel = getContentPanel();
86		{
87			gradientEditor = new GradientEditor() {
88				public void handleSelected (Color color) {
89					GradientPanel.this.setColor(color);
90				}
91			};
92			contentPanel.add(gradientEditor, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER,
93				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 10));
94		}
95		{
96			hueSlider = new ColorSlider(new Color[] {Color.red, Color.yellow, Color.green, Color.cyan, Color.blue, Color.magenta,
97				Color.red}) {
98				protected void colorPicked () {
99					saturationSlider.setColors(new Color[] {new Color(Color.HSBtoRGB(getPercentage(), 1, 1)), Color.white});
100					updateColor();
101				}
102			};
103			contentPanel.add(hueSlider, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER,
104				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));
105		}
106		{
107			saturationSlider = new ColorSlider(new Color[] {Color.red, Color.white}) {
108				protected void colorPicked () {
109					updateColor();
110				}
111			};
112			contentPanel.add(saturationSlider, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
113				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 6), 0, 0));
114		}
115		{
116			lightnessSlider = new ColorSlider(new Color[0]) {
117				protected void colorPicked () {
118					updateColor();
119				}
120			};
121			contentPanel.add(lightnessSlider, new GridBagConstraints(2, 3, 1, 1, 1, 0.0, GridBagConstraints.CENTER,
122				GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
123		}
124		{
125			colorPanel = new JPanel() {
126				public Dimension getPreferredSize () {
127					Dimension size = super.getPreferredSize();
128					size.width = 52;
129					return size;
130				}
131			};
132			contentPanel.add(colorPanel, new GridBagConstraints(0, 2, 1, 2, 0.0, 0.0, GridBagConstraints.CENTER,
133				GridBagConstraints.BOTH, new Insets(3, 0, 0, 6), 0, 0));
134		}
135
136		colorPanel.addMouseListener(new MouseAdapter() {
137			public void mouseClicked (MouseEvent e) {
138				Color color = JColorChooser.showDialog(colorPanel, "Set Color", colorPanel.getBackground());
139				if (color != null) setColor(color);
140			}
141		});
142		colorPanel.setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
143	}
144
145	public void setColor (Color color) {
146		float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
147		hueSlider.setPercentage(hsb[0]);
148		saturationSlider.setPercentage(1 - hsb[1]);
149		lightnessSlider.setPercentage(1 - hsb[2]);
150		colorPanel.setBackground(color);
151	}
152
153	void updateColor () {
154		Color color = new Color(Color.HSBtoRGB(hueSlider.getPercentage(), 1 - saturationSlider.getPercentage(), 1));
155		lightnessSlider.setColors(new Color[] {color, Color.black});
156		color = new Color(Color.HSBtoRGB(hueSlider.getPercentage(), 1 - saturationSlider.getPercentage(),
157			1 - lightnessSlider.getPercentage()));
158		colorPanel.setBackground(color);
159		gradientEditor.setColor(color);
160
161		float[] colors = new float[gradientEditor.colors.size() * 3];
162		int i = 0;
163		for (Color c : gradientEditor.colors) {
164			colors[i++] = c.getRed() / 255f;
165			colors[i++] = c.getGreen() / 255f;
166			colors[i++] = c.getBlue() / 255f;
167		}
168		float[] percentages = new float[gradientEditor.percentages.size()];
169		i = 0;
170		for (Float percent : gradientEditor.percentages)
171			percentages[i++] = percent;
172		value.setColors(colors);
173		value.setTimeline(percentages);
174	}
175
176	public class GradientEditor extends JPanel {
177		ArrayList<Color> colors = new ArrayList();
178		ArrayList<Float> percentages = new ArrayList();
179
180		int handleWidth = 12;
181		int handleHeight = 12;
182		int gradientX = handleWidth / 2;
183		int gradientY = 0;
184		int gradientWidth;
185		int gradientHeight;
186		int dragIndex = -1;
187		int selectedIndex;
188
189		public GradientEditor () {
190			setPreferredSize(new Dimension(100, 30));
191
192			addMouseListener(new MouseAdapter() {
193				public void mousePressed (MouseEvent event) {
194					dragIndex = -1;
195					int mouseX = event.getX();
196					int mouseY = event.getY();
197					int y = gradientY + gradientHeight;
198					for (int i = 0, n = colors.size(); i < n; i++) {
199						int x = gradientX + (int)(percentages.get(i) * gradientWidth) - handleWidth / 2;
200						if (mouseX >= x && mouseX <= x + handleWidth && mouseY >= gradientY && mouseY <= y + handleHeight) {
201							dragIndex = selectedIndex = i;
202							handleSelected(colors.get(selectedIndex));
203							repaint();
204							break;
205						}
206					}
207				}
208
209				public void mouseReleased (MouseEvent event) {
210					if (dragIndex != -1) {
211						dragIndex = -1;
212						repaint();
213					}
214				}
215
216				public void mouseClicked (MouseEvent event) {
217					int mouseX = event.getX();
218					int mouseY = event.getY();
219					if (event.getClickCount() == 2) {
220						if (percentages.size() <= 1) return;
221						if (selectedIndex == -1 || selectedIndex == 0) return;
222						int y = gradientY + gradientHeight;
223						int x = gradientX + (int)(percentages.get(selectedIndex) * gradientWidth) - handleWidth / 2;
224						if (mouseX >= x && mouseX <= x + handleWidth && mouseY >= gradientY && mouseY <= y + handleHeight) {
225							percentages.remove(selectedIndex);
226							colors.remove(selectedIndex);
227							selectedIndex--;
228							dragIndex = selectedIndex;
229							if (percentages.size() == 2) percentages.set(1, 1f);
230							handleSelected(colors.get(selectedIndex));
231							repaint();
232						}
233						return;
234					}
235					if (mouseX < gradientX || mouseX > gradientX + gradientWidth) return;
236					if (mouseY < gradientY || mouseY > gradientY + gradientHeight) return;
237					float percent = (event.getX() - gradientX) / (float)gradientWidth;
238					if (percentages.size() == 1) percent = 1f;
239					for (int i = 0, n = percentages.size(); i <= n; i++) {
240						if (i == n || percent < percentages.get(i)) {
241							percentages.add(i, percent);
242							colors.add(i, colors.get(i - 1));
243							dragIndex = selectedIndex = i;
244							handleSelected(colors.get(selectedIndex));
245							repaint();
246							break;
247						}
248					}
249				}
250			});
251			addMouseMotionListener(new MouseMotionAdapter() {
252				public void mouseDragged (MouseEvent event) {
253					if (dragIndex == -1 || dragIndex == 0 || dragIndex == percentages.size() - 1) return;
254					float percent = (event.getX() - gradientX) / (float)gradientWidth;
255					percent = Math.max(percent, percentages.get(dragIndex - 1) + 0.01f);
256					percent = Math.min(percent, percentages.get(dragIndex + 1) - 0.01f);
257					percentages.set(dragIndex, percent);
258					repaint();
259				}
260			});
261		}
262
263		public void setColor (Color color) {
264			if (selectedIndex == -1) return;
265			colors.set(selectedIndex, color);
266			repaint();
267		}
268
269		public void handleSelected (Color color) {
270		}
271
272		protected void paintComponent (Graphics graphics) {
273			super.paintComponent(graphics);
274			Graphics2D g = (Graphics2D)graphics;
275			int width = getWidth() - 1;
276			int height = getHeight();
277
278			gradientWidth = width - handleWidth;
279			gradientHeight = height - 16;
280
281			g.translate(gradientX, gradientY);
282			for (int i = 0, n = colors.size() == 1 ? 1 : colors.size() - 1; i < n; i++) {
283				Color color1 = colors.get(i);
284				Color color2 = colors.size() == 1 ? color1 : colors.get(i + 1);
285				float percent1 = percentages.get(i);
286				float percent2 = colors.size() == 1 ? 1 : percentages.get(i + 1);
287				int point1 = (int)(percent1 * gradientWidth);
288				int point2 = (int)Math.ceil(percent2 * gradientWidth);
289				g.setPaint(new GradientPaint(point1, 0, color1, point2, 0, color2, false));
290				g.fillRect(point1, 0, point2 - point1, gradientHeight);
291			}
292			g.setPaint(null);
293			g.setColor(Color.black);
294			g.drawRect(0, 0, gradientWidth, gradientHeight);
295
296			int y = gradientHeight;
297			int[] yPoints = new int[3];
298			yPoints[0] = y;
299			yPoints[1] = y + handleHeight;
300			yPoints[2] = y + handleHeight;
301			int[] xPoints = new int[3];
302			for (int i = 0, n = colors.size(); i < n; i++) {
303				int x = (int)(percentages.get(i) * gradientWidth);
304				xPoints[0] = x;
305				xPoints[1] = x - handleWidth / 2;
306				xPoints[2] = x + handleWidth / 2;
307				if (i == selectedIndex) {
308					g.setColor(colors.get(i));
309					g.fillPolygon(xPoints, yPoints, 3);
310					g.fillRect(xPoints[1], yPoints[1] + 2, handleWidth + 1, 2);
311					g.setColor(Color.black);
312				}
313				g.drawPolygon(xPoints, yPoints, 3);
314			}
315			g.translate(-gradientX, -gradientY);
316		}
317	}
318
319	static public class ColorSlider extends JPanel {
320		Color[] paletteColors;
321		JSlider slider;
322		private ColorPicker colorPicker;
323
324		public ColorSlider (Color[] paletteColors) {
325			this.paletteColors = paletteColors;
326			setLayout(new GridBagLayout());
327			{
328				slider = new JSlider(0, 1000, 0);
329				slider.setPaintTrack(false);
330				add(slider, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
331					new Insets(0, 6, 0, 6), 0, 0));
332			}
333			{
334				colorPicker = new ColorPicker();
335				add(colorPicker, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
336					GridBagConstraints.HORIZONTAL, new Insets(0, 6, 0, 6), 0, 0));
337			}
338
339			slider.addChangeListener(new ChangeListener() {
340				public void stateChanged (ChangeEvent event) {
341					colorPicked();
342				}
343			});
344		}
345
346		public Dimension getPreferredSize () {
347			Dimension size = super.getPreferredSize();
348			size.width = 10;
349			return size;
350		}
351
352		public void setPercentage (float percent) {
353			slider.setValue((int)(1000 * percent));
354		}
355
356		public float getPercentage () {
357			return slider.getValue() / 1000f;
358		}
359
360		protected void colorPicked () {
361		}
362
363		public void setColors (Color[] colors) {
364			paletteColors = colors;
365			repaint();
366		}
367
368		public class ColorPicker extends JPanel {
369			public ColorPicker () {
370				addMouseListener(new MouseAdapter() {
371					public void mouseClicked (MouseEvent event) {
372						slider.setValue((int)(event.getX() / (float)getWidth() * 1000));
373					}
374				});
375			}
376
377			protected void paintComponent (Graphics graphics) {
378				Graphics2D g = (Graphics2D)graphics;
379				int width = getWidth() - 1;
380				int height = getHeight() - 1;
381				for (int i = 0, n = paletteColors.length - 1; i < n; i++) {
382					Color color1 = paletteColors[i];
383					Color color2 = paletteColors[i + 1];
384					float point1 = i / (float)n * width;
385					float point2 = (i + 1) / (float)n * width;
386					g.setPaint(new GradientPaint(point1, 0, color1, point2, 0, color2, false));
387					g.fillRect((int)point1, 0, (int)Math.ceil(point2 - point1), height);
388				}
389				g.setPaint(null);
390				g.setColor(Color.black);
391				g.drawRect(0, 0, width, height);
392			}
393		}
394	}
395}
396