OSDN Git Service

Updates to 3D gallery source.
[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 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),
49                 paint);
50         SRC_PAINT.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
51     }
52
53     public PopupMenu(Context context) {
54         mPopupTexture = new PopupTexture(context);
55         setHidden(true);
56     }
57
58     public void setListener(Listener listener) {
59         mListener = listener;
60     }
61
62     public void setOptions(Option[] options) {
63         close(false);
64         mOptions = options;
65         mNeedsLayout = true;
66     }
67
68     public void showAtPoint(int pointX, int pointY, int outerWidth, int outerHeight) {
69         // Compute layout if needed.
70         if (mNeedsLayout) {
71             layout();
72         }
73         // Try to center the popup over the target point.
74         int width = (int) mWidth;
75         int height = (int) mHeight;
76         int widthOver2 = width / 2;
77         int x = pointX - widthOver2;
78         int y = pointY + POPUP_Y_OFFSET - height;
79         int clampedX = Shared.clamp(x, 0, outerWidth - width);
80         int triangleWidthOver2 = mPopupTexture.mTriangleBottom.getWidth() / 2;
81         mPopupTexture.mTriangleX = Shared.clamp(widthOver2 + (x - clampedX) - triangleWidthOver2, POPUP_TRIANGLE_X_MARGIN, width
82                 - POPUP_TRIANGLE_X_MARGIN * 2);
83         mPopupTexture.setNeedsDraw();
84         setPosition(clampedX, y);
85
86         // Fade in the menu if it is not already visible, otherwise snap to the new location.
87         // if (!mShow) {
88         mShow = true;
89         setHidden(false);
90         mShowAnim.setValue(0);
91         mShowAnim.animateValue(1f, 0.4f, SystemClock.uptimeMillis());
92         // }
93     }
94
95     public void close(boolean fadeOut) {
96         if (mShow) {
97             if (fadeOut) {
98                 mShowAnim.animateValue(0, 0.3f, SystemClock.uptimeMillis());
99             } else {
100                 mShowAnim.setValue(0);
101             }
102             mShow = false;
103             mSelectedItem = -1;
104         }
105
106     }
107
108     @Override
109     public void generate(RenderView view, RenderView.Lists lists) {
110         lists.blendedList.add(this);
111         lists.hitTestList.add(this);
112         lists.systemList.add(this);
113         lists.updateList.add(this);
114     }
115
116     @Override
117     protected void onSizeChanged() {
118         super.onSizeChanged();
119         mPopupTexture.setSize((int) mWidth, (int) mHeight);
120     }
121
122     @Override
123     protected void onSurfaceCreated(RenderView view, GL11 gl) {
124         close(false);
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         if (show && showRatio < 1f) {
177             // Animate the scale as well for the open animation.
178             float scale;
179             float split = 0.7f;
180             if (showRatio < split) {
181                 scale = 0.8f + 0.3f * showRatio / split;
182             } else {
183                 scale = 1f + ((1f - showRatio) / (1f - split)) * 0.1f;
184             }
185             mPopupTexture.drawWithEffect(view, gl, x, y, 0.5f, 0.65f, showRatio, scale);
186         } else {
187             if (showRatio < 1f) {
188                 view.setAlpha(showRatio);
189             }
190             mPopupTexture.draw(view, gl, x, y);
191             if (showRatio < 1f) {
192                 view.resetColor();
193             }
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 }