OSDN Git Service

f200061a7c9b27b249dbf81385a30bd798a29436
[android-x86/packages-apps-Gallery2.git] / src / com / cooliris / media / PopupMenu.java
1 package com.cooliris.media;
2
3 import javax.microedition.khronos.opengles.GL11;
4
5 import com.cooliris.media.R;
6
7 import android.content.Context;
8 import android.content.res.Resources;
9 import android.graphics.Bitmap;
10 import android.graphics.BitmapFactory;
11 import android.graphics.Canvas;
12 import android.graphics.NinePatch;
13 import android.graphics.Paint;
14 import android.graphics.PorterDuff;
15 import android.graphics.PorterDuffXfermode;
16 import android.graphics.Rect;
17 import android.graphics.drawable.Drawable;
18 import android.os.SystemClock;
19 import android.text.TextPaint;
20 import android.view.MotionEvent;
21
22 public final class PopupMenu extends Layer {
23     private static final int POPUP_TRIANGLE_EXTRA_HEIGHT = 14;
24     private static final int POPUP_TRIANGLE_X_MARGIN = 16;
25     private static final int POPUP_Y_OFFSET = 20;
26     private static final Paint SRC_PAINT = new Paint();
27     private static final int PADDING_LEFT = 10 + 5;
28     private static final int PADDING_TOP = 10 + 3;
29     private static final int PADDING_RIGHT = 10 + 5;
30     private static final int PADDING_BOTTOM = 30 + 10;
31     private static final int ICON_TITLE_MIN_WIDTH = 100;
32     private static final IconTitleDrawable.Config ICON_TITLE_CONFIG;
33
34     private final PopupTexture mPopupTexture;
35     private Listener mListener = null;
36     private Option[] mOptions = {};
37     private boolean mNeedsLayout = false;
38     private boolean mShow = false;
39     private final FloatAnim mShowAnim = new FloatAnim(0f);
40     private int mRowHeight = 36;
41     private int mSelectedItem = -1;
42
43     static {
44         TextPaint paint = new TextPaint();
45         paint.setTextSize(17f * Gallery.PIXEL_DENSITY);
46         paint.setColor(0xffffffff);
47         paint.setAntiAlias(true);
48         ICON_TITLE_CONFIG = new IconTitleDrawable.Config((int)(45 * Gallery.PIXEL_DENSITY), (int)(34 * Gallery.PIXEL_DENSITY), paint);
49         SRC_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
50     }
51
52     public PopupMenu(Context context) {
53         mPopupTexture = new PopupTexture(context);
54         setHidden(true);
55     }
56
57     public void setListener(Listener listener) {
58         mListener = listener;
59     }
60
61     public void setOptions(Option[] options) {
62         close(false);
63         mOptions = options;
64         mNeedsLayout = true;
65     }
66
67     public void showAtPoint(int pointX, int pointY, int outerWidth, int outerHeight) {
68         // Compute layout if needed.
69         if (mNeedsLayout) {
70             layout();
71         }
72         // Try to center the popup over the target point.
73         int width = (int)mWidth;
74         int height = (int)mHeight;
75         int widthOver2 = width / 2;
76         int x = pointX - widthOver2;
77         int y = pointY + POPUP_Y_OFFSET - height;
78         int clampedX = Shared.clamp(x, 0, outerWidth - width);
79         int triangleWidthOver2 = mPopupTexture.mTriangleBottom.getWidth() / 2;
80         mPopupTexture.mTriangleX = Shared.clamp(widthOver2 + (x - clampedX) - triangleWidthOver2, POPUP_TRIANGLE_X_MARGIN, width
81                 - POPUP_TRIANGLE_X_MARGIN * 2);
82         mPopupTexture.setNeedsDraw();
83         setPosition(clampedX, y);
84
85         // Fade in the menu if it is not already visible, otherwise snap to the new location.
86         // if (!mShow) {
87         mShow = true;
88         setHidden(false);
89         mShowAnim.setValue(0);
90         mShowAnim.animateValue(1f, 0.4f, SystemClock.uptimeMillis());
91         // }
92     }
93
94     public void close(boolean fadeOut) {
95         if (mShow) {
96             if (fadeOut) {
97                 mShowAnim.animateValue(0, 0.3f, SystemClock.uptimeMillis());
98             } else {
99                 mShowAnim.setValue(0);
100             }
101             mShow = false;
102             mSelectedItem = -1;
103         }
104         
105     }
106
107     @Override
108     public void generate(RenderView view, RenderView.Lists lists) {
109         lists.blendedList.add(this);
110         lists.hitTestList.add(this);
111         lists.systemList.add(this);
112         lists.updateList.add(this);
113     }
114
115     @Override
116     protected void onSizeChanged() {
117         super.onSizeChanged();
118         mPopupTexture.setSize((int)mWidth, (int)mHeight);
119     }
120
121     @Override
122     protected void onSurfaceCreated(RenderView view, GL11 gl) {
123         close(false);
124         mPopupTexture.resetTexture();
125     }
126
127     @Override
128     public boolean onTouchEvent(MotionEvent event) {
129         int hit = hitTestOptions((int) event.getX(), (int) event.getY());
130         switch (event.getAction()) {
131         case MotionEvent.ACTION_DOWN:
132         case MotionEvent.ACTION_MOVE:
133             setSelectedItem(hit);
134             break;
135         case MotionEvent.ACTION_UP:
136             if (hit != -1 && mSelectedItem == hit) {
137                 mOptions[hit].mAction.run();
138                 if (mListener != null) {
139                     mListener.onSelectionClicked(this, hit);
140                 }
141             }
142         case MotionEvent.ACTION_CANCEL:
143             setSelectedItem(-1);
144             break;
145         }
146         return true;
147     }
148
149     private void setSelectedItem(int hit) {
150         if (mSelectedItem != hit) {
151             mSelectedItem = hit;
152             mPopupTexture.setNeedsDraw();
153             if (mListener != null) {
154                 mListener.onSelectionChanged(this, hit);
155             }
156         }
157     }
158     
159     @Override
160     public boolean update(RenderView view, float timeElapsed) {
161         return (mShowAnim.getTimeRemaining(SystemClock.uptimeMillis()) > 0);
162      }
163
164     @Override
165     public void renderBlended(RenderView view, GL11 gl) {
166         // Hide the layer if the close animation is complete.
167         float showRatio = mShowAnim.getValue(SystemClock.uptimeMillis());
168         boolean show = mShow;
169         if (showRatio < 0.003f && !show) {
170             setHidden(true);
171         }
172
173         // Draw the selection menu with the show animation.
174         int x = (int) mX;
175         int y = (int) mY;
176
177         if (show && showRatio < 1f) {
178             // Animate the scale as well for the open animation.
179             float scale;
180             float split = 0.7f;
181             if (showRatio < split) {
182                 scale = 0.8f + 0.3f * showRatio / split;
183             } else {
184                 scale = 1f + ((1f - showRatio) / (1f - split)) * 0.1f;
185             }
186             mPopupTexture.drawWithEffect(view, gl, x, y, 0.5f, 0.65f, showRatio, scale);
187         } else {
188             if (showRatio < 1f) {
189                 view.setAlpha(showRatio);
190             }
191             mPopupTexture.draw(view, gl, x, y);
192             if (showRatio < 1f) {
193                 view.resetColor();
194             }
195         }
196     }
197
198     private void layout() {
199         // Mark as not needing layout.
200         mNeedsLayout = false;
201
202         // Measure the menu options.
203         Option[] options = mOptions;
204         int numOptions = options.length;
205         int maxWidth = (int)(ICON_TITLE_MIN_WIDTH * Gallery.PIXEL_DENSITY);
206         for (int i = 0; i != numOptions; ++i) {
207             Option option = options[i];
208             IconTitleDrawable drawable = option.mDrawable;
209             if (drawable == null) {
210                 drawable = new IconTitleDrawable(option.mTitle, option.mIcon, ICON_TITLE_CONFIG);
211                 option.mDrawable = drawable;
212             }
213             int width = drawable.getIntrinsicWidth();
214             if (width > maxWidth) {
215                 maxWidth = width;
216             }
217         }
218
219         // Layout the menu options.
220         int rowHeight = (int)(mRowHeight * Gallery.PIXEL_DENSITY);
221         int left = (int)(PADDING_LEFT * Gallery.PIXEL_DENSITY);
222         int top = (int)(PADDING_TOP * Gallery.PIXEL_DENSITY);
223         int right = left + maxWidth;
224         for (int i = 0; i != numOptions; ++i) {
225             Option option = options[i];
226             IconTitleDrawable drawable = option.mDrawable;
227             option.mBottom = top + rowHeight;
228             drawable.setBounds(left, top, right, option.mBottom);
229             top += rowHeight;
230         }
231
232         // Resize the popup menu.
233         setSize(right + PADDING_RIGHT * Gallery.PIXEL_DENSITY, top + PADDING_BOTTOM * Gallery.PIXEL_DENSITY);
234         
235     }
236
237     private int hitTestOptions(int x, int y) {
238         Option[] options = mOptions;
239         int numOptions = options.length;
240         x -= mX;
241         y -= mY;
242         if (numOptions != 0 && x >= 0 && x < mWidth && y >= 0) {
243             for (int i = 0; i != numOptions; ++i) {
244                 if (y < options[i].mBottom) {
245                     return i;
246                 }
247             }
248         }
249         return -1;
250     }
251
252     public interface Listener {
253         void onSelectionChanged(PopupMenu menu, int selectedIndex);
254
255         void onSelectionClicked(PopupMenu menu, int selectedIndex);
256     }
257
258     public static final class Option {
259         private final String mTitle;
260         private final Drawable mIcon;
261         private final Runnable mAction;
262         private IconTitleDrawable mDrawable = null;
263         private int mBottom;
264
265         public Option(String title, Drawable icon, Runnable action) {
266             mTitle = title;
267             mIcon = icon;
268             mAction = action;
269         }
270     }
271
272     private final class PopupTexture extends CanvasTexture {
273         private final NinePatch mBackground;
274         private final NinePatch mHighlightSelected;
275         private final Bitmap mTriangleBottom;
276         private final Rect mBackgroundRect = new Rect();
277         private int mTriangleX = 0;
278
279         public PopupTexture(Context context) {
280             super(Bitmap.Config.ARGB_8888);
281             Resources resources = context.getResources();
282             Bitmap background = BitmapFactory.decodeResource(resources, R.drawable.popup);
283             mBackground = new NinePatch(background, background.getNinePatchChunk(), null);
284             Bitmap highlightSelected = BitmapFactory.decodeResource(resources, R.drawable.popup_option_selected);
285             mHighlightSelected = new NinePatch(highlightSelected, highlightSelected.getNinePatchChunk(), null);
286             mTriangleBottom = BitmapFactory.decodeResource(resources, R.drawable.popup_triangle_bottom);
287         }
288
289         @Override
290         protected void onSizeChanged() {
291             mBackgroundRect.set(0, 0, getWidth(), getHeight() - (int)(POPUP_TRIANGLE_EXTRA_HEIGHT * Gallery.PIXEL_DENSITY));
292         }
293
294         @Override
295         protected void renderCanvas(Canvas canvas, Bitmap backing, int width, int height) {
296             // Draw the background.
297             backing.eraseColor(0);
298             mBackground.draw(canvas, mBackgroundRect, SRC_PAINT);
299
300             // Stamp the popup triangle over the appropriate region ignoring alpha.
301             Bitmap triangle = mTriangleBottom;
302             canvas.drawBitmap(triangle, mTriangleX, height - triangle.getHeight() - 1, SRC_PAINT);
303
304             // Draw the selection / focus highlight.
305             Option[] options = mOptions;
306             int selectedItem = mSelectedItem;
307             if (selectedItem != -1) {
308                 Option option = options[selectedItem];
309                 mHighlightSelected.draw(canvas, option.mDrawable.getBounds());
310             }
311
312             // Draw icons and titles.
313             int numOptions = options.length;
314             for (int i = 0; i != numOptions; ++i) {
315                 options[i].mDrawable.draw(canvas);
316             }
317         }
318
319     }
320 }