package totalcross.ui; import totalcross.sys.*; import totalcross.ui.anim.*; import totalcross.ui.event.*; import totalcross.ui.gfx.*; import totalcross.ui.image.*; /** This is a top menu like those on Android. It opens and closes using animation and fading effects. * @since TotalCross 3.03 */ public class TopMenu extends Window implements PathAnimation.AnimationFinished { public static interface AnimationListener { public void onAnimationFinished(); } /** The percentage of the area used for the icon and the caption */ public static int percIcon = 20, percCap = 80; private Control []items; private ScrollContainer sc; private int animDir; private int selected=-1; /** Set to false to disable the close when pressing in a button of the menu. */ public boolean autoClose = true; /** Defines the animation delay */ public int totalTime; /** The percentage of the screen that this TopMenu will take: LEFT/RIGHT will take 50% of the screen's width, * other directions will take 80% of the screen's width. Must be ser before calling <code>popup()</code>. */ public int percWidth; private AnimationListener alist; /** The width in pixels instead of percentage of screen's width. */ public int widthInPixels; public static class Item extends Container { Control tit; Image icon; ImageControl ic; /** Used when you want to fully customize your Item by extending this class. */ protected Item() { setBackForeColors(UIColors.topmenuBack,UIColors.topmenuFore); } /** Pass a Control and optionally an icon */ public Item(Control c, Image icon) { this(); this.tit = c; this.icon = icon; } /** Creates a Label and optionally an icon */ public Item(String cap, Image icon) { this(new Label((String)cap,LEFT),icon); } public void initUI() { int itemH = fmH + Edit.prefH; int perc = percCap; if (icon == null) perc = 100; else { try {ic = new ImageControl(Settings.enableWindowTransitionEffects ? icon.getSmoothScaledInstance(itemH,itemH) : icon.getHwScaledInstance(itemH,itemH)); ic.centerImage = true;} catch (ImageException e) {} add(ic == null ? (Control)new Spacer(itemH,itemH) : (Control)ic,LEFT,TOP,PARENTSIZE+percIcon,FILL); } add(tit, AFTER+(icon==null? tit instanceof Label ? itemH:0:0),TOP,PARENTSIZE+perc-(tit instanceof Label?10:0),FILL,ic); } public void onEvent(Event e) { if (e.type == PenEvent.PEN_UP && !hadParentScrolled()) { int bc = backColor; setBackColors(Color.brighter(bc)); repaintNow(); Vm.sleep(100); postPressedEvent(); setBackColors(bc); } } private void setBackColors(int b) { setBackColor(b); for (Control child = children; child != null; child = child.next) if (child instanceof Label || child instanceof ImageControl) // changing ComboBox back does not work well... should think in an alternative child.setBackColor(b); } } /** @param animDir LEFT, RIGHT, TOP, BOTTOM, CENTER */ public TopMenu(Control []items, int animDir) { super(null,ROUND_BORDER); this.items = items; this.animDir = animDir; titleGap = 0; fadeOtherWindows = false; uiAdjustmentsBasedOnFontHeightIsSupported = false; borderColor = UIColors.separatorFore; setBackForeColors(UIColors.separatorFore,UIColors.topmenuFore); } public void popup() { setRect(); super.popup(); } private void setRect() { int ww = widthInPixels != 0 ? widthInPixels : SCREENSIZE+(percWidth > 0 ? percWidth : 50); switch (animDir) { case LEFT: case RIGHT: setRect(animDir,TOP,ww,FILL); break; default: setRect(100000,100000,ww,WILL_RESIZE); break; } } final public void initUI() { int gap = 2; int n = items.length; int itemH = n == 1 ? Math.max(items[0].getPreferredHeight(),getClientRect().height-4) : fmH*2; int prefH = n * itemH + gap * n; boolean isLR = animDir == LEFT || animDir == RIGHT; add(sc = new ScrollContainer(false,true),LEFT+1,TOP+2,FILL-1,isLR ? PARENTSIZE+100 : Math.min(prefH, Settings.screenHeight-fmH*2)-2); sc.setBackColor(backColor); for (int i = 0;; i++) { Control tmi = items[i]; tmi.appId = i+1; sc.add(tmi,LEFT,AFTER,FILL,itemH); if (i == n-1) break; Ruler r = new Ruler(Ruler.HORIZONTAL,false); r.setBackColor(backColor); sc.add(r,LEFT,AFTER,FILL, gap); } if (!isLR) resizeHeight(); } public void onEvent(Event e) { switch (e.type) { case ControlEvent.PRESSED: if (autoClose && e.target != this && ((Control)e.target).isChildOf(this)) { selected = ((Control)e.target).appId-1; postPressedEvent(); unpop(); } break; case PenEvent.PEN_DRAG_END: DragEvent de = (DragEvent)e; if (sameDirection(animDir, de.direction) && de.xTotal >= width/2) unpop(); break; } } private boolean sameDirection(int animDir, int dragDir) { if (animDir < 0) animDir = -animDir; return (dragDir == DragEvent.LEFT && animDir == LEFT) || (dragDir == DragEvent.RIGHT && animDir == RIGHT) || (dragDir == DragEvent.UP && animDir == TOP) || (dragDir == DragEvent.DOWN && animDir == BOTTOM); } public void screenResized() { setRect(); removeAll(); initUI(); // used for custom containers for (int i = 0; i < items.length; i++) if (items[i].asContainer != null) { Control []c = items[i].asContainer.getChildren(); for (int j = c.length; --j >= 0;) c[j].reposition(); } } protected boolean onClickedOutside(PenEvent event) { if (event.type == PenEvent.PEN_UP) unpop(); return true; } public void unpop() { unpop(null); } public void unpop(AnimationListener alist) { this.alist = alist; try { if (animDir == CENTER) FadeAnimation.create(this,false,this,totalTime).start(); else PathAnimation.create(this,-animDir,this,totalTime).with(FadeAnimation.create(this,false,null,totalTime)).start(); } catch (Exception e) { if (Settings.onJavaSE) e.printStackTrace(); super.unpop(); // no animation, just unpop } } public void onAnimationFinished(ControlAnimation anim) { super.unpop(); } public void postUnpop() { super.postUnpop(); if (alist != null) alist.onAnimationFinished(); } public void onPopup() { selected = -1; try { if (animDir == CENTER) { resetSetPositions(); setRect(CENTER,CENTER,KEEP,KEEP); FadeAnimation.create(this,true,null,totalTime).start(); } else PathAnimation.create(this,animDir,null,totalTime).with(FadeAnimation.create(this,true,null,totalTime)).start(); } catch (Exception e) { if (Settings.onJavaSE) e.printStackTrace(); // no animation, just popup } } public int getSelectedIndex() { return selected; } }