JBCardLayout, CardLayout able to swipe pages
[idea/community.git] / platform / platform-impl / src / com / intellij / ui / JBCardLayout.java
1 /*
2  * Copyright 2000-2012 JetBrains s.r.o.
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 package com.intellij.ui;
17
18 import org.jetbrains.annotations.NotNull;
19 import org.jetbrains.annotations.Nullable;
20
21 import javax.swing.*;
22 import java.awt.*;
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.io.IOException;
26 import java.util.Iterator;
27 import java.util.LinkedHashMap;
28 import java.util.Map;
29
30 /**
31  * User: Vassiliy.Kudryashov
32  */
33 public class JBCardLayout extends CardLayout {
34   public enum SwipeDirection {FORWARD, BACKWARD, AUTO}
35
36   private Map<String, Component> myMap = new LinkedHashMap<String, Component>();
37   private int mySwipeTime = 400;//default value, provide setter if need
38   private int mySwipeSteps = 20;//default value, provide setter if need
39   private final Timer myTimer = new Timer(Math.max(1, mySwipeTime / mySwipeSteps), null);
40   private Component mySwipeFrom = null;
41   private Component mySwipeTo = null;
42
43   public JBCardLayout() {
44     this(0, 0);
45   }
46
47   public JBCardLayout(int hgap, int vgap) {
48     super(hgap, vgap);
49   }
50
51   @Override
52   public void addLayoutComponent(String name, Component comp) {
53     super.addLayoutComponent(name, comp);
54     myMap.put(name, comp);
55   }
56
57   @Override
58   public void removeLayoutComponent(Component comp) {
59     stopSwipeIfNeed();
60     super.removeLayoutComponent(comp);
61     for (Iterator<Map.Entry<String, Component>> iterator = myMap.entrySet().iterator(); iterator.hasNext(); ) {
62       Map.Entry<String, Component> entry = iterator.next();
63       if (entry.getValue() == comp) {
64         iterator.remove();
65         break;
66       }
67     }
68   }
69
70   @Override
71   public void layoutContainer(Container parent) {
72     if (!myTimer.isRunning()) {
73       super.layoutContainer(parent);
74     }
75   }
76
77   public void swipe(@NotNull final Container parent, @NotNull final String name, @NotNull SwipeDirection direction) {
78     stopSwipeIfNeed();
79     mySwipeFrom = findVisible(parent);
80     mySwipeTo = myMap.get(name);
81     if (mySwipeFrom == null || mySwipeTo == null || mySwipeFrom == mySwipeTo) {
82       return;
83     }
84     final boolean isForward;
85     if (direction == SwipeDirection.AUTO) {
86       boolean b = true;
87       for (Component component : myMap.values()) {
88         if (component == mySwipeFrom || component == mySwipeTo) {
89           b = component == mySwipeFrom;
90           break;
91         }
92       }
93       isForward = b;
94     }
95     else {
96       isForward = direction == SwipeDirection.FORWARD;
97     }
98     final double[] linearProgress = new double[1];
99     linearProgress[0] = 0;
100     final long startTime = System.currentTimeMillis();
101     ActionListener listener = new ActionListener() {
102       @Override
103       public void actionPerformed(ActionEvent e) {
104         long timePassed = System.currentTimeMillis() - startTime;
105         if (timePassed >= mySwipeTime) {
106           show(parent, name);
107           return;
108         }
109         linearProgress[0] = Math.min(1, Math.max(0, (float)timePassed / mySwipeTime));
110         double naturalProgress = (1 - Math.cos(Math.PI * linearProgress[0])) / 2;
111         Rectangle bounds = parent.getBounds();
112         bounds.setLocation(0, 0);
113         Rectangle r = new Rectangle(bounds);
114         int x = (int)((naturalProgress * r.width));
115         r.translate(isForward ? -x : x, 0);
116         mySwipeFrom.setBounds(r);
117         Rectangle r2 = new Rectangle(bounds);
118         r2.translate(isForward ? r2.width - x : x - r2.width, 0);
119         mySwipeTo.setVisible(true);
120         mySwipeTo.setBounds(r2);
121         parent.repaint();
122       }
123     };
124     for (ActionListener actionListener : myTimer.getActionListeners()) {
125       myTimer.removeActionListener(actionListener);
126     }
127     myTimer.addActionListener(listener);
128     myTimer.start();
129   }
130
131   private void stopSwipeIfNeed() {
132     if (myTimer.isRunning()) {
133       myTimer.stop();
134       mySwipeFrom = null;
135       if (mySwipeTo != null) {
136         mySwipeTo.setVisible(false);
137         mySwipeTo = null;
138       }
139     }
140   }
141
142   @Nullable
143   private static Component findVisible(Container parent) {
144     for (int i = 0; i < parent.getComponentCount(); i++) {
145       Component component = parent.getComponent(i);
146       if (component.isVisible()) return component;
147     }
148     return null;
149   }
150
151   @Override
152   public void first(Container parent) {
153     stopSwipeIfNeed();
154     super.first(parent);
155   }
156
157   @Override
158   public void next(Container parent) {
159     stopSwipeIfNeed();
160     super.next(parent);
161   }
162
163   @Override
164   public void previous(Container parent) {
165     stopSwipeIfNeed();
166     super.previous(parent);
167   }
168
169   @Override
170   public void last(Container parent) {
171     stopSwipeIfNeed();
172     super.last(parent);
173   }
174
175   @Override
176   public void show(Container parent, String name) {
177     stopSwipeIfNeed();
178     super.show(parent, name);
179   }
180
181   public static void main(String[] args) throws IOException {
182     final JBCardLayout cardLayout = new JBCardLayout();
183
184     JFrame f = new JFrame();
185     JPanel p = new JPanel(new BorderLayout());
186     final JPanel centerPanel = new JPanel(cardLayout);
187     for (int i = 0; i < 10; i++) {
188       JLabel l = new JLabel("Page #" + (i + 1), SwingConstants.CENTER);
189       l.setOpaque(true);
190       int red = 50 + (int)(Math.random() * 100);
191       int green = 50 + (int)(Math.random() * 100);
192       int blue = 50 + (int)(Math.random() * 100);
193       l.setForeground(new Color(red, green, blue));
194       red = 255 - (int)(Math.random() * 55);
195       green = 255 - (int)(Math.random() * 55);
196       blue = 255 - (int)(Math.random() * 55);
197       l.setBackground(new Color(red, green, blue));
198       l.setFont(l.getFont().deriveFont(40F));
199       centerPanel.add(l, "page" + i);
200     }
201     final int[] cursor = new int[1];
202     JButton prevButton = new JButton("<<");
203     JButton nextButton = new JButton(">>");
204     prevButton.addActionListener(new ActionListener() {
205       @Override
206       public void actionPerformed(ActionEvent e) {
207         cursor[0]--;
208         if (cursor[0] < 0) {
209           cursor[0] = 9;
210         }
211         cardLayout.swipe(centerPanel, "page" + cursor[0], SwipeDirection.BACKWARD);
212       }
213     });
214     nextButton.addActionListener(new ActionListener() {
215       @Override
216       public void actionPerformed(ActionEvent e) {
217         cursor[0] = (cursor[0] + 1) % 10;
218         cardLayout.swipe(centerPanel, "page" + cursor[0], SwipeDirection.FORWARD);
219       }
220     });
221
222     p.setLayout(new BorderLayout());
223     p.add(prevButton, BorderLayout.WEST);
224     p.add(centerPanel, BorderLayout.CENTER);
225     p.add(nextButton, BorderLayout.EAST);
226     f.setContentPane(p);
227     f.setSize(new Dimension(600, 800));
228     f.setLocationRelativeTo(null);
229     f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
230     f.setVisible(true);
231   }
232 }