<commit_before>//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2013, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.ui;

import java.util.ArrayList;
import java.util.List;

import playn.core.Asserts;
import playn.core.Events;
import playn.core.Layer;
import playn.core.Pointer;
import playn.core.Pointer.Event;
import pythagoras.f.Point;
import react.Signal;
import react.SignalView;
import react.Slot;
import tripleplay.anim.Animation;
import tripleplay.anim.Animator;
import tripleplay.ui.MenuItem.ShowText;

/**
 * Holds a collection of {@link MenuItem}s, dispatching a {@link Menu#itemTriggered} signal
 * when one is selected and triggered. Normally used in conjunction with {@link MenuHost} to popup
 * the menu (in its own {@code Root}), manage animations, track user input, and handle
 * cancellation.
 *
 * <p>Note that a menu can contain arbitrary {@code Element}s, but only those that are
 * {@code MenuItem}s are eligible for triggering.</p>
 *
 * TODO: support escape key to cancel; probably in MenuHost
 * TODO: scrolling support for really big menus
 * TODO: support/implement full screen menus - this is probably what most phone apps will want
 * TODO: select menu items on hover
 */
public class Menu extends Elements<Menu>
{
    /**
     * Produces an animation for a menu.
     */
    public interface AnimFn
    {
        /**
         * For the given menu and animator, adds an animation to the given animator and returns it.
         * TODO: how can more than one animation be supported? seems Animation should have a join()
         * method in addition to then()
         */
        Animation go (Menu menu, Animator animator);
    }

    /** Generic animation to fade in a menu using the layer alpha. */
    public static AnimFn FADE_IN = new AnimFn() {
        public Animation go (Menu menu, Animator animator) {
            menu.layer.setAlpha(0);
            return animator.tweenAlpha(menu.layer).to(1).easeIn().in(200);
        }
    };

    /** Generic animation to fade out a menu using the layer alpha. */
    public static AnimFn FADE_OUT = new AnimFn() {
        public Animation go (Menu menu, Animator animator) {
            return animator.tweenAlpha(menu.layer).to(0).easeIn().in(40);
        }
    };

    /** The opening animation function for the menu. */
    public static Style<AnimFn> OPENER = Style.newStyle(true, FADE_IN);

    /** The closing animation function for the menu. */
    public static Style<AnimFn> CLOSER = Style.newStyle(true, FADE_OUT);

    /**
     * Creates a new menu using the given layout for its elements.
     */
    public Menu (Layout layout) {
        super(layout);

        // use a hit tester "eater" to pretend our layer covers all its siblings
        layer.setHitTester(new Layer.HitTester() {
            @Override public Layer hitTest (Layer layer, Point p) {
                Layer descendant = layer.hitTestDefault(p);
                return descendant == null ? layer : descendant;
            }
        });

        // deactivate the menu on any pointer events (children will still get theirs)
        layer.addListener(new Pointer.Adapter() {
            @Override public void onPointerStart (Pointer.Event event) {
                if (event.hit() == layer) deactivate();
            }
        });

        childAdded().connect(_descendantAdded);
        childRemoved().connect(_descendantRemoved);
    }

    /**
     * Creates a new menu using the given layout and styles.
     */
    public Menu (Layout layout, Styles styles) {
        this(layout);
        setStyles(styles);
    }

    /**
     * Creates a new menu using the given layout and style bindings.
     */
    public Menu (Layout layout, Style.Binding<?>... styles) {
        this(layout);
        setStyles(styles);
    }

    /**
     * Gets the signal that is dispatched when the menu is closed and no longer usable. This
     * occurs if an item is triggered or if the menu is manually cancelled (using
     * {@link #deactivate()}).
     */
    public SignalView<Menu> deactivated () {
        return _deactivated;
    }

    /**
     * Opens this menu, using an animation created by the resolved {@link #OPENER} style. Once the
     * animation is finished, the user can view the {@code MenuItem} choices. When one is selected
     * and dispatched via the {@link #itemTriggered} signal, the menu is deactivated automatically.
     */
    public void activate () {
        // already active, nothing to do
        if (_active) return;

        // Undefunct
        _defunct = false;

        Runnable doActivation = new Runnable() {
            @Override public void run () {
                // skip to the end!
                fastForward();

                // animate the menu opening
                _complete = new Runnable() {
                    @Override public void run () {
                        onOpened();
                    }
                };
                _anim = open().then().action(_complete).handle();
            }
        };

        // postpone the activation if we need validation
        if (isSet(Flag.VALID)) doActivation.run();
        else _postLayout = doActivation;
    }

    /**
     * Closes this menu, using an animation created by the resolved {@link #CLOSER} style. This is
     * normally called automatically when the user clicks off the menu or triggers one of its
     * {@code MenuItem}s. After the animation is complete, the {@link #deactivated()} signal will
     * be dispatched.
     */
    public void deactivate () {
        // skip to the end!
        fastForward();

        // disable input and animate closure
        _active = false;
        _defunct = true;
        _complete = new Runnable() {
            @Override public void run () {
                onClosed();
            }
        };
        _anim = close().then().action(_complete).handle();
    }

    /**
     * Gets the signal that is dispatched when a menu item is selected.
     */
    public SignalView<MenuItem> itemTriggered () {
        return _itemTriggered;
    }

    /** Tests if this menu's position should be adjusted by the host such that the menu's bounds
     * lies within the requested area. */
    protected boolean automaticallyConfine () {
        return true;
    }

    @Override protected Class<?> getStyleClass () {
        return Menu.class;
    }

    @Override protected void layout () {
        super.layout();

        // and now activate if it was previously requested and we weren't yet valid
        if (_postLayout != null) {
            _postLayout.run();
            _postLayout = null;
        }
    }

    /** Creates an animation to move the menu's layer (and its children) into the open state.
     * By default, simply resolves the {@link #OPENER} style and calls {@link AnimFn#go}.
     * Subclasses can hook in here if needed. */
    protected Animation open () {
        return resolveStyle(OPENER).go(this, _animator);
    }

    /** Creates an animation to move the menu's layer (and its children) into the open state.
     * By default, simply resolves the {@link #CLOSER} style and calls {@link AnimFn#go}.
     * Subclasses can hook in here if needed. */<commit_msg>Delegate to subclasses to decide if hits are absorbed.<commit_after>//
// Triple Play - utilities for use in PlayN-based games
// Copyright (c) 2011-2013, Three Rings Design, Inc. - All rights reserved.
// http://github.com/threerings/tripleplay/blob/master/LICENSE

package tripleplay.ui;

import java.util.ArrayList;
import java.util.List;

import playn.core.Asserts;
import playn.core.Events;
import playn.core.Layer;
import playn.core.Pointer;
import playn.core.Pointer.Event;
import pythagoras.f.Point;
import react.Signal;
import react.SignalView;
import react.Slot;
import tripleplay.anim.Animation;
import tripleplay.anim.Animator;
import tripleplay.ui.MenuItem.ShowText;

/**
 * Holds a collection of {@link MenuItem}s, dispatching a {@link Menu#itemTriggered} signal
 * when one is selected and triggered. Normally used in conjunction with {@link MenuHost} to popup
 * the menu (in its own {@code Root}), manage animations, track user input, and handle
 * cancellation.
 *
 * <p>Note that a menu can contain arbitrary {@code Element}s, but only those that are
 * {@code MenuItem}s are eligible for triggering.</p>
 *
 * TODO: support escape key to cancel; probably in MenuHost
 * TODO: scrolling support for really big menus
 * TODO: support/implement full screen menus - this is probably what most phone apps will want
 * TODO: select menu items on hover
 */
public class Menu extends Elements<Menu>
{
    /**
     * Produces an animation for a menu.
     */
    public interface AnimFn
    {
        /**
         * For the given menu and animator, adds an animation to the given animator and returns it.
         * TODO: how can more than one animation be supported? seems Animation should have a join()
         * method in addition to then()
         */
        Animation go (Menu menu, Animator animator);
    }

    /** Generic animation to fade in a menu using the layer alpha. */
    public static AnimFn FADE_IN = new AnimFn() {
        public Animation go (Menu menu, Animator animator) {
            menu.layer.setAlpha(0);
            return animator.tweenAlpha(menu.layer).to(1).easeIn().in(200);
        }
    };

    /** Generic animation to fade out a menu using the layer alpha. */
    public static AnimFn FADE_OUT = new AnimFn() {
        public Animation go (Menu menu, Animator animator) {
            return animator.tweenAlpha(menu.layer).to(0).easeIn().in(40);
        }
    };

    /** The opening animation function for the menu. */
    public static Style<AnimFn> OPENER = Style.newStyle(true, FADE_IN);

    /** The closing animation function for the menu. */
    public static Style<AnimFn> CLOSER = Style.newStyle(true, FADE_OUT);

    /**
     * Creates a new menu using the given layout for its elements.
     */
    public Menu (Layout layout) {
        super(layout);

        // use a hit tester "eater" to pretend our layer covers all its siblings
        layer.setHitTester(new Layer.HitTester() {
            @Override public Layer hitTest (Layer layer, Point p) {
                Layer descendant = layer.hitTestDefault(p);
                return descendant == null ? absorbHits() ? layer : null : descendant;
            }
        });

        // deactivate the menu on any pointer events (children will still get theirs)
        layer.addListener(new Pointer.Adapter() {
            @Override public void onPointerStart (Pointer.Event event) {
                if (event.hit() == layer) deactivate();
            }
        });

        childAdded().connect(_descendantAdded);
        childRemoved().connect(_descendantRemoved);
    }

    /**
     * Creates a new menu using the given layout and styles.
     */
    public Menu (Layout layout, Styles styles) {
        this(layout);
        setStyles(styles);
    }

    /**
     * Creates a new menu using the given layout and style bindings.
     */
    public Menu (Layout layout, Style.Binding<?>... styles) {
        this(layout);
        setStyles(styles);
    }

    /**
     * Gets the signal that is dispatched when the menu is closed and no longer usable. This
     * occurs if an item is triggered or if the menu is manually cancelled (using
     * {@link #deactivate()}).
     */
    public SignalView<Menu> deactivated () {
        return _deactivated;
    }

    /**
     * Opens this menu, using an animation created by the resolved {@link #OPENER} style. Once the
     * animation is finished, the user can view the {@code MenuItem} choices. When one is selected
     * and dispatched via the {@link #itemTriggered} signal, the menu is deactivated automatically.
     */
    public void activate () {
        // already active, nothing to do
        if (_active) return;

        // Undefunct
        _defunct = false;

        Runnable doActivation = new Runnable() {
            @Override public void run () {
                // skip to the end!
                fastForward();

                // animate the menu opening
                _complete = new Runnable() {
                    @Override public void run () {
                        onOpened();
                    }
                };
                _anim = open().then().action(_complete).handle();
            }
        };

        // postpone the activation if we need validation
        if (isSet(Flag.VALID)) doActivation.run();
        else _postLayout = doActivation;
    }

    /**
     * Closes this menu, using an animation created by the resolved {@link #CLOSER} style. This is
     * normally called automatically when the user clicks off the menu or triggers one of its
     * {@code MenuItem}s. After the animation is complete, the {@link #deactivated()} signal will
     * be dispatched.
     */
    public void deactivate () {
        // skip to the end!
        fastForward();

        // disable input and animate closure
        _active = false;
        _defunct = true;
        _complete = new Runnable() {
            @Override public void run () {
                onClosed();
            }
        };
        _anim = close().then().action(_complete).handle();
    }

    /**
     * Gets the signal that is dispatched when a menu item is selected.
     */
    public SignalView<MenuItem> itemTriggered () {
        return _itemTriggered;
    }

    /** Tests if this menu's position should be adjusted by the host such that the menu's bounds
     * lies within the requested area. */
    protected boolean automaticallyConfine () {
        return true;
    }

    /** Tests if this menu should trap all positional events. */
    protected boolean absorbHits () {
        return true;
    }

    @Override protected Class<?> getStyleClass () {
        return Menu.class;
    }

    @Override protected void layout () {
        super.layout();

        // and now activate if it was previously requested and we weren't yet valid
        if (_postLayout != null) {
            _postLayout.run();
            _postLayout = null;
        }
    }

    /** Creates an animation to move the menu's layer (and its children) into the open state.
     * By default, simply resolves the {@link #OPENER} style and calls {@link AnimFn#go}.
     * Subclasses can hook in here if needed. */
    protected Animation open () {
        return resolveStyle(OPENER).go(this, _animator);
    }

    /** Creates an animation to move the menu's layer (and its children) into the open state.
     * By default, simply resolves the {@link #CLOSER} style and calls {@link AnimFn#go}.
     * Subclasses can hook in here if needed. */
{'fraction_non_alphanumeric': 0.09489740820734341, 'fraction_numerical': 0.0021598272138228943, 'mean_word_length': 2.2817275747508305, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '121118', 'n_tokens_mistral': 4000, 'n_tokens_neox': 3837, 'n_words': 1510}