PieMenu.java revision 4be9bc7f7f38723ae8c4ca1d3203de212cf214bd
1376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb/* 2376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Copyright (C) 2010 The Android Open Source Project 3376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 4376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 5376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * you may not use this file except in compliance with the License. 6376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * You may obtain a copy of the License at 7376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 8376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 9376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 10376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Unless required by applicable law or agreed to in writing, software 11376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 12376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * See the License for the specific language governing permissions and 14376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * limitations under the License. 15376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 16376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 17376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpackage com.android.browser.view; 18376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 19376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport com.android.browser.R; 20376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 21376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.Context; 22376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.res.Resources; 23376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Canvas; 24376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Paint; 25376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Path; 26376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Point; 27376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.PointF; 28376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Rect; 29376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.RectF; 30376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.util.AttributeSet; 31376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.MotionEvent; 32376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.View; 334be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reckimport android.view.ViewGroup; 34376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.widget.FrameLayout; 35376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 36376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.ArrayList; 37376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.HashMap; 38376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.List; 39376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.Map; 40376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 41376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpublic class PieMenu extends FrameLayout { 42376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 43376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private static final int RADIUS_GAP = 10; 44376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 45376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public interface PieController { 46376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 47376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * called before menu opens to customize menu 48376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * returns if pie state has been changed 49376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 50376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onOpen(); 51376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 52376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Point mCenter; 53376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadius; 54376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadiusInc; 55376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mSlop; 56376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 57376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mOpen; 58376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Paint mPaint; 59376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Paint mSelectedPaint; 60376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private PieController mController; 61376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 62376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Map<View, List<View>> mMenu; 63376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private List<View> mStack; 64376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 65376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mDirty; 66376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 67376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 68376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 69376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 70376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param defStyle 71376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 72376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs, int defStyle) { 73376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs, defStyle); 74376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 75376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 76376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 77376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 78376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 79376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 80376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 81376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs) { 82376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs); 83376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 84376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 85376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 86376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 87376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 88376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 89376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context) { 90376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context); 91376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 92376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 93376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 94376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void init(Context ctx) { 95376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb this.setTag(new MenuTag(0)); 96376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack = new ArrayList<View>(); 97376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(this); 98376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Resources res = ctx.getResources(); 99376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadius = (int) res.getDimension(R.dimen.qc_radius); 100376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_inc); 101376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSlop = (int) res.getDimension(R.dimen.qc_slop); 102376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint = new Paint(); 103376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint.setAntiAlias(true); 104376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mPaint.setColor(res.getColor(R.color.qc_slice_normal)); 105376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint = new Paint(); 106376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint.setAntiAlias(true); 107376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSelectedPaint.setColor(res.getColor(R.color.qc_slice_active)); 108376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = false; 109376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu = new HashMap<View, List<View>>(); 110376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setWillNotDraw(false); 111376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setDrawingCacheEnabled(false); 112376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter = new Point(0,0); 113376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 114376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 115376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 116376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setController(PieController ctl) { 117376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mController = ctl; 118376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 119376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 120376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setRadius(int r) { 121376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadius = r; 122376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb requestLayout(); 123376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 124376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 125376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setRadiusIncrement(int ri) { 126376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mRadiusInc = ri; 127376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb requestLayout(); 128376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 129376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 130376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 131376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * add a menu item to another item as a submenu 132376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param item 133376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param parent 134376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 135376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void addItem(View item, View parent) { 136376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 137376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs == null) { 138376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb subs = new ArrayList<View>(); 139376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.put(parent, subs); 140376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 141376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb subs.add(item); 142376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = new MenuTag(((MenuTag) parent.getTag()).level + 1); 143376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.setTag(tag); 144376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 145376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 146376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void addItem(View view) { 147376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // add the item to the pie itself 148376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb addItem(view, this); 149376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 150376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 151376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void removeItem(View view) { 152376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(view); 153376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.remove(view); 154376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View p : mMenu.keySet()) { 155376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> sl = mMenu.get(p); 156376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (sl != null) { 157376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb sl.remove(view); 158376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 159376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 160376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 161376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 162376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems(View parent) { 163376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.remove(parent); 164376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 165376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View sub: subs) { 166376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb clearItems(sub); 167376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 168376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 169376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 170376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 171376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems() { 172376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mMenu.clear(); 173376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 174376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 175376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 176376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void show(boolean show) { 177376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = show; 178376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 179376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mController != null) { 180376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb boolean changed = mController.onOpen(); 181376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 182376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 183376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 184376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (!show) { 185376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // hide sub items 186376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.clear(); 187376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(this); 188376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 189376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 190376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 191376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 192376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setCenter(int x, int y) { 193376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (x < mSlop) { 194376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = 0; 195376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 196376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = getWidth(); 197376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 198376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.y = y; 199376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 200376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 201376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean onTheLeft() { 202376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return mCenter.x < mSlop; 203376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 204376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 205376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 206376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb protected void onDraw(Canvas canvas) { 207376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 208376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int radius = mRadius; 209376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // start in the center for 0 level menu 210376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float anchor = (float) Math.PI / 2; 211376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb PointF angles = new PointF(); 212376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int state = canvas.save(); 213376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 214376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // left handed 215376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.scale(-1, 1); 216376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 217376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View parent : mStack) { 218376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 219376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 220376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setGeometry(anchor, subs.size(), angles); 221376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 222376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb anchor = drawSlices(canvas, subs, radius, angles.x, angles.y); 223376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb radius += mRadiusInc + RADIUS_GAP; 224376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 225376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.restoreToCount(state); 226376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = false; 227376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 228376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 229376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 230376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 231376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * draw the set of slices 232376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param canvas 233376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param items 234376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param radius 235376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param start 236376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param sweep 237376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @return the angle of the selected slice 238376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 239376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private float drawSlices(Canvas canvas, List<View> items, int radius, 240376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float start, float sweep) { 241376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float angle = start + sweep / 2; 242376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // gap between slices in degrees 243376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float gap = 1f; 244376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float newanchor = 0f; 245376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View item : items) { 246376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mDirty) { 247376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.measure(item.getLayoutParams().width, 248376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.getLayoutParams().height); 249376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int w = item.getMeasuredWidth(); 250376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int h = item.getMeasuredHeight(); 251376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int x = (int) (radius * Math.sin(angle)); 252376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int y = mCenter.y - (int) (radius * Math.cos(angle)) - h / 2; 253376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 254376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x + x - w / 2; 255376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 256376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x - w / 2; 257376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 258376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.layout(x, y, x + w, y + h); 259376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 260376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float itemstart = angle - sweep / 2; 261376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int inner = radius - mRadiusInc / 2; 262376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int outer = radius + mRadiusInc / 2; 263376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Path slice = makeSlice(getDegrees(itemstart) - gap, 264376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb getDegrees(itemstart + sweep) + gap, 265376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb outer, inner, mCenter); 266376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) item.getTag(); 267376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.start = itemstart; 268376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.sweep = sweep; 269376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.inner = inner; 270376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb tag.outer = outer; 271376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 272376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Paint p = item.isPressed() ? mSelectedPaint : mPaint; 273376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.drawPath(slice, p); 274376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int state = canvas.save(); 275376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (onTheLeft()) { 276376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.scale(-1, 1); 277376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 278376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.translate(item.getX(), item.getY()); 279376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb item.draw(canvas); 280376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb canvas.restoreToCount(state); 281376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mStack.contains(item)) { 282376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // item is anchor for sub menu 283376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb newanchor = angle; 284376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 285376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb angle += sweep; 286376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 287376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return newanchor; 288376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 289376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 290376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 291376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * converts a 292376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param angle from 0..PI to Android degrees (clockwise starting at 3 o'clock) 293376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @return skia angle 294376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 295376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private float getDegrees(double angle) { 296376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return (float) (270 - 180 * angle / Math.PI); 297376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 298376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 299376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Path makeSlice(float startangle, float endangle, int outerradius, 300376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int innerradius, Point center) { 301376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb RectF bb = new RectF(center.x - outerradius, center.y - outerradius, 302376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb center.x + outerradius, center.y + outerradius); 303376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb RectF bbi = new RectF(center.x - innerradius, center.y - innerradius, 304376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb center.x + innerradius, center.y + innerradius); 305376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Path path = new Path(); 306376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.arcTo(bb, startangle, endangle - startangle, true); 307376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.arcTo(bbi, endangle, startangle - endangle); 308376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb path.close(); 309376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return path; 310376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 311376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 312376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 313376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * all angles are 0 .. MATH.PI where 0 points up, and rotate counterclockwise 314376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * set the startangle and slice sweep in result 315376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param anchorangle : angle at which the menu is anchored 316376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param nslices 317376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param result : x : start, y : sweep 318376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 319376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setGeometry(float anchorangle, int nslices, PointF result) { 320376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float span = (float) Math.min(anchorangle, Math.PI - anchorangle); 321376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float sweep = 2 * span / (nslices + 1); 322376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb result.x = anchorangle - span + sweep / 2; 323376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb result.y = sweep; 324376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 325376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 326376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // touch handling for pie 327376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 328376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View mCurrentView; 329376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Rect mHitRect = new Rect(); 330376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 331376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 332376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onTouchEvent(MotionEvent evt) { 333376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float x = evt.getX(); 334376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float y = evt.getY(); 335376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int action = evt.getActionMasked(); 336376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int edges = evt.getEdgeFlags(); 337376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (MotionEvent.ACTION_DOWN == action) { 338376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if ((x > getWidth() - mSlop) || (x < mSlop)) { 339376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setCenter((int) x, (int) y); 340376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(true); 341376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 342376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 343376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_UP == action) { 344376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 345376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View v = mCurrentView; 346376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 347376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (v != null) { 348376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb v.performClick(); 349376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 350376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 351376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 352376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 353376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_CANCEL == action) { 354376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 355376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 356376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 357376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 358376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 359376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_MOVE == action) { 360bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF polar = getPolar(x, y); 361bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb if (polar.y > mRadius + 2 * mRadiusInc) { 362bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb show(false); 363bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb deselect(); 3644be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck evt.setAction(MotionEvent.ACTION_DOWN); 3654be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck if (getParent() != null) { 3664be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck ((ViewGroup) getParent()).dispatchTouchEvent(evt); 3674be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck } 368bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return false; 369bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 370bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb View v = findView(polar); 371376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != v) { 372376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb onEnter(v); 373376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 374376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 375376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 376376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // always re-dispatch event 377376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 378376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 379376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 380376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 381376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * enter a slice for a view 382376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * updates model only 383376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param view 384376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 385376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void onEnter(View view) { 386376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // deselect 387376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != null) { 388376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (getLevel(mCurrentView) >= getLevel(view)) { 389376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView.setPressed(false); 390376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 391376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 392376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (view != null) { 393376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // clear up stack 394376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) view.getTag(); 395376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int i = mStack.size() - 1; 396376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb while (i > 0) { 397376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb View v = mStack.get(i); 398376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (((MenuTag) v.getTag()).level >= tag.level) { 399376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb v.setPressed(false); 400376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.remove(i); 401376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 402376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb break; 403376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 404376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb i--; 405376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 406376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> items = mMenu.get(view); 407376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (items != null) { 408376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mStack.add(view); 409376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mDirty = true; 410376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 411376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb view.setPressed(true); 412376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 413376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView = view; 414376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 415376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 416376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void deselect() { 417376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCurrentView != null) { 418376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView.setPressed(false); 419376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 420376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCurrentView = null; 421376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 422376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 423376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int getLevel(View v) { 424376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (v == null) return -1; 425376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return ((MenuTag) v.getTag()).level; 426376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 427376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 428bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private PointF getPolar(float x, float y) { 429bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF res = new PointF(); 430376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // get angle and radius from x/y 431bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.PI / 2; 432376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x; 433376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCenter.x < mSlop) { 434376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = -x; 435376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 436376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb y = mCenter.y - y; 437bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.y = (float) Math.sqrt(x * x + y * y); 438376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (y > 0) { 439bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.asin(x / res.y); 440376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (y < 0) { 441bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) (Math.PI - Math.asin(x / res.y )); 442376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 443bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return res; 444bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 445bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb 446bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb /** 447bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * 448bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @param polar x: angle, y: dist 449bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @return 450bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb */ 451bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private View findView(PointF polar) { 452376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // find the matching item: 453376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View parent : mStack) { 454376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb List<View> subs = mMenu.get(parent); 455376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (subs != null) { 456376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb for (View item : subs) { 457376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb MenuTag tag = (MenuTag) item.getTag(); 458bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb if ((tag.inner < polar.y) 459bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.outer > polar.y) 460bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.start < polar.x) 461bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb && (tag.start + tag.sweep > polar.x)) { 462376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return item; 463376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 464376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 465376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 466376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 467376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return null; 468376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 469376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 470376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb class MenuTag { 471376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 472376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int level; 473376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float start; 474376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float sweep; 475376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int inner; 476376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int outer; 477376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 478376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public MenuTag(int l) { 479376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb level = l; 480376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 481376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 482376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 483376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 484376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb} 485