/* * Copyright (C) 2015 The Android Open Source Project * * 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. */ import com.android.tools.chartlib.AnimatedComponent; import com.android.tools.chartlib.EventData; import com.android.tools.chartlib.SunburstComponent; import com.android.tools.chartlib.TimelineComponent; import com.android.tools.chartlib.TimelineData; import com.android.tools.chartlib.ValuedTreeNode; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.LayoutManager; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JTabbedPane; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.tree.DefaultMutableTreeNode; public class AnimatedComponentVisualTests extends JDialog { private List<AnimatedComponent> mComponents = new LinkedList<AnimatedComponent>(); public AnimatedComponentVisualTests() { JPanel contentPane = new JPanel(new BorderLayout()); JButton close = new JButton("Close"); JTabbedPane tabs = new JTabbedPane(); tabs.addTab("PieChart", getPieChartExample()); tabs.addTab("Timeline", getTimelineExample()); contentPane.setPreferredSize(new Dimension(1280, 1024)); contentPane.add(tabs, BorderLayout.CENTER); JPanel bottom = new JPanel(new BorderLayout()); bottom.add(close, BorderLayout.EAST); contentPane.add(bottom, BorderLayout.SOUTH); JPanel controls = new JPanel(); controls.setLayout(new BoxLayout(controls, BoxLayout.Y_AXIS)); controls.add(Box.createRigidArea(new Dimension(100, 20))); final JCheckBox debug = new JCheckBox("Debug"); debug.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { for (AnimatedComponent component : mComponents) { component.setDrawDebugInfo(debug.isSelected()); } } }); controls.add(debug); final JButton step = new JButton("Step"); step.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { for (AnimatedComponent component : mComponents) { component.step(); } } }); final JCheckBox update = new JCheckBox("Update"); update.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { for (AnimatedComponent component : mComponents) { component.setUpdateData(update.isSelected()); } step.setEnabled(!update.isSelected()); } }); update.setSelected(true); step.setEnabled(false); final JCheckBox dark = new JCheckBox("Dark"); dark.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { setDarkMode(dark.isSelected()); } }); controls.add(dark); controls.add(update); controls.add(step); contentPane.add(controls, BorderLayout.WEST); setDarkMode(false); setContentPane(contentPane); setModal(true); getRootPane().setDefaultButton(close); close.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dispose(); } }); } private void setDarkMode(boolean dark) { for (AnimatedComponent c : mComponents) { c.setBackground(dark ? new Color(60, 63, 65) : new Color(244, 244, 244)); } } interface Value { void set(int v); int get(); } private static JPanel createVaribleSlider(String name, final int a, final int b, final Value value) { JPanel panel = new JPanel(new BorderLayout()); final JLabel text = new JLabel(); final JSlider slider = new JSlider(a, b); ChangeListener listener = new ChangeListener() { @Override public void stateChanged(ChangeEvent changeEvent) { value.set(slider.getValue()); text.setText(String.format("%d [%d,%d]", slider.getValue(), a, b)); } }; slider.setValue(value.get()); listener.stateChanged(null); slider.addChangeListener(listener); panel.add(slider, BorderLayout.CENTER); panel.add(new JLabel(name + ": "), BorderLayout.WEST); panel.add(text, BorderLayout.EAST); panel.setAlignmentX(Component.LEFT_ALIGNMENT); return panel; } private JPanel createControlledPane(JPanel panel, AnimatedComponent animated) { panel.setLayout(new BorderLayout()); mComponents.add(animated); panel.add(animated, BorderLayout.CENTER); JPanel controls = new JPanel(); LayoutManager manager = new BoxLayout(controls, BoxLayout.Y_AXIS); controls.setLayout(manager); controls.setPreferredSize(new Dimension(300, 800)); panel.add(controls, BorderLayout.WEST); return controls; } static class DataNode extends DefaultMutableTreeNode implements ValuedTreeNode { private int mCount; private int mValue; public DataNode() { this(0, 0); } public DataNode(int count, int value) { mCount = count; mValue = value; } @Override public int getCount() { return mCount; } @Override public int getValue() { return mValue; } public void add(int count, int value) { mCount += count; mValue += value; if (parent instanceof DataNode) { ((DataNode)parent).add(count, value); } } public void addDataNode(DataNode dataNode) { super.add(dataNode); add(dataNode.getCount(), dataNode.getValue()); } } private JPanel getPieChartExample() { final DataNode data = new DataNode(); data.addDataNode(new DataNode(1, 10)); final SunburstComponent layout = new SunburstComponent(data); JPanel panel = new JPanel(); JPanel controls = createControlledPane(panel, layout); final JLabel info = new JLabel("<No information yet>"); panel.add(info, BorderLayout.SOUTH); controls.add(createVaribleSlider("Gap", 0, 200, new Value() { @Override public void set(int v) { layout.setGap(v); } @Override public int get() { return (int) layout.getGap(); } })); controls.add(createVaribleSlider("Size", 0, 200, new Value() { @Override public void set(int v) { layout.setSliceWidth(v); } @Override public int get() { return (int) layout.getSliceWidth(); } })); controls.add(createVaribleSlider("Angle", 0, 360, new Value() { @Override public void set(int v) { layout.setAngle(v); } @Override public int get() { return (int) layout.getAngle(); } })); controls.add(createVaribleSlider("Start", 0, 360, new Value() { @Override public void set(int v) { layout.setStart(v); } @Override public int get() { return (int) layout.getStart(); } })); controls.add(createVaribleSlider("Fixed", 1, 100, new Value() { @Override public void set(int v) { layout.setFixed(v); } @Override public int get() { return (int) layout.getFixed(); } })); controls.add(createVaribleSlider("Separator", 0, 100, new Value() { @Override public void set(int v) { layout.setSeparator(v); } @Override public int get() { return (int) layout.getSeparator(); } })); controls.add(createButton("Generate", new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { generateLayoutData((DataNode) layout.getData(), 5); } })); controls.add(createButton("Tree A", new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { DataNode g = new DataNode(); g.addDataNode(createTree(1)); g.addDataNode(createValue()); g.addDataNode(createTree(1)); g.addDataNode(createValue()); g.addDataNode(createTree(0)); layout.setData(g); } })); controls.add(createButton("Tree B", new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { DataNode g = new DataNode(); g.addDataNode(createValue()); g.addDataNode(createValue()); g.addDataNode(createTree(0)); layout.setData(g); } })); controls.add(createButton("Value", new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { DataNode g = new DataNode(); g.addDataNode(new DataNode(1, (int) (Math.random() * 50))); layout.setData(g); } })); controls.add(createCheckbox("Auto size", new ItemListener() { @Override public void itemStateChanged(ItemEvent itemEvent) { layout.setAutoSize(itemEvent.getStateChange() == ItemEvent.SELECTED); } })); controls.add( new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE), new Dimension(300, Integer.MAX_VALUE))); layout.addSelectionListener(new SunburstComponent.SliceSelectionListener() { @Override public void valueChanged(SunburstComponent.SliceSelectionEvent e) { ValuedTreeNode node = e.getNode(); info.setText(node == null ? "<No selection>" : String.format("Value %d Count %d", node.getValue(), node.getCount())); } }); return panel; } private static DataNode createValue() { return new DataNode(1, (int)(Math.random() * 50)); } private static DataNode createTree(int depth) { DataNode b = depth == 0 ? createValue() : createTree(depth - 1); DataNode c = depth == 0 ? createValue() : createTree(depth - 1); DataNode a = new DataNode(); a.addDataNode(b); a.addDataNode(c); return a; } private static Component createButton(String label, ActionListener action) { JButton button = new JButton(label); button.addActionListener(action); button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height)); return button; } private static Component createCheckbox(String label, ItemListener action) { JCheckBox button = new JCheckBox(label); button.addItemListener(action); button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMaximumSize().height)); return button; } private static void generateLayoutData(DataNode data, int maxDepth) { Random random = new Random(); int branch = random.nextInt(9) + 1; for (int i = 0; i < branch; i++) { int value = random.nextInt(1024); if (maxDepth > 0 && random.nextInt(4) == 0) { DataNode group = new DataNode(); group.add(new DataNode(1, value)); generateLayoutData(group, maxDepth - 1); data.addDataNode(group); } else { data.addDataNode(new DataNode(1, value)); } } } private JPanel getTimelineExample() { final TimelineData data = new TimelineData(2, 2000); final EventData events = new EventData(); final int streams = 2; final AtomicInteger variance = new AtomicInteger(10); final AtomicInteger delay = new AtomicInteger(100); final AtomicInteger type = new AtomicInteger(0); new Thread() { @Override public void run() { super.run(); try { float[] values = new float[streams]; while (true) { int v = variance.get(); for (int i = 0; i < streams; i++) { float delta = (float) Math.random() * variance.get() - v * 0.5f; values[i] = Math.max(0, delta + values[i]); } synchronized (data) { data.add(System.currentTimeMillis(), type.get() + (v == 0 ? 1 : 0), Arrays.copyOf(values, streams)); } Thread.sleep(delay.get()); } } catch (InterruptedException e) { } } }.start(); final TimelineComponent timeline = new TimelineComponent(data, events, 1.0f, 10.0f, 1000.0f, 10.0f); timeline.configureStream(0, "Data 0", new Color(0x78abd9)); timeline.configureStream(1, "Data 1", new Color(0xbaccdc)); timeline.configureUnits("@"); timeline.configureEvent(1, 0, UIManager.getIcon("Tree.leafIcon"), new Color(0x92ADC6), new Color(0x2B4E8C), false); timeline.configureEvent(2, 1, UIManager.getIcon("Tree.leafIcon"), new Color(255, 191, 176), new Color(76, 14, 29), true); timeline.configureType(1, TimelineComponent.Style.SOLID); timeline.configureType(2, TimelineComponent.Style.DASHED); final JPanel panel = new JPanel(); final JPanel controls = createControlledPane(panel, timeline); controls.add(createVaribleSlider("Delay", 10, 5000, new Value() { @Override public void set(int v) { delay.set(v); } @Override public int get() { return delay.get(); } })); controls.add(createVaribleSlider("Variance", 0, 50, new Value() { @Override public void set(int v) { variance.set(v); } @Override public int get() { return variance.get(); } })); controls.add(createVaribleSlider("Type", 0, 2, new Value() { @Override public void set(int v) { type.set(v); } @Override public int get() { return type.get(); } })); controls.add(createEventButton(1, events, variance)); controls.add(createEventButton(1, events, null)); controls.add(createEventButton(2, events, variance)); controls.add(new Box.Filler(new Dimension(0, 0), new Dimension(300, Integer.MAX_VALUE), new Dimension(300, Integer.MAX_VALUE))); panel.add(timeline, BorderLayout.CENTER); return panel; } private Component createEventButton(final int type, final EventData events, final AtomicInteger variance) { final String start = "Start " + (variance != null ? "blocking " : "") + "event type " + type; final String stop = "Stop event type " + type; return createButton(start, new ActionListener() { EventData.Event event = null; int var = 0; @Override public void actionPerformed(ActionEvent actionEvent) { JButton button = (JButton) actionEvent.getSource(); if (event != null) { event.stop(System.currentTimeMillis()); event = null; if (variance != null) { variance.set(var); } button.setText(start); } else { event = events.start(System.currentTimeMillis(), type); if (variance != null) { var = variance.get(); variance.set(0); } button.setText(stop); } } }); } public static void main(String[] args) { AnimatedComponentVisualTests dialog = new AnimatedComponentVisualTests(); dialog.pack(); dialog.setVisible(true); System.exit(0); } }