<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.layout;

import java.util.HashMap;
import java.util.Map;

import pythagoras.f.Dimension;
import pythagoras.f.IDimension;
import pythagoras.f.Rectangle;
import tripleplay.ui.Element;
import tripleplay.ui.Elements;
import tripleplay.ui.Layout;
import tripleplay.ui.Style;

/**
 * Arranges up to 5 elements, one central and one on each edge. Added elements must have a
 * constraint from the class' listing (e.g. {@link BorderLayout#CENTER}), which determines the
 * position in the layout and stretching.
 *
 * <p>This is how the layout looks. Note north/south and east/west behavior is not quite symmetric
 * because east and west fit between the bottom of the north and top of the south:</p>
 *
 * <p><pre>
 *     |-----------------------------|
 *     |            north            |
 *     |-----------------------------|
 *     |      |               |      |
 *     |      |               |      |
 *     |      |               |      |
 *     | west |    center     | east |
 *     |      |               |      |
 *     |      |               |      |
 *     |-----------------------------|
 *     |            south            |
 *     |-----------------------------|
 * </pre></p>
 *
 * When an element is not stretched, it obeys the {@link tripleplay.ui.Style.HAlign} and {@link
 * tripleplay.ui.Style.VAlign} bindings.
 */
public class BorderLayout extends Layout
{
    /** Constraint to position an element in the center of its parent. The element is stretched in
     * both directions to take up available space. If {@link Constraint#unstretched} is used, the
     * element will be aligned in both directions using its preferred size and the {@link
     * tripleplay.ui.Style.HAlign} and {@link tripleplay.ui.Style.VAlign} bindings. */
    public static final Constraint CENTER = Position.CENTER.stretched;

    /** Constraint to position an element along the top edge of its parent. The element is
     * stretched horizontally and uses its preferred height. If {@link Constraint#unstretched} is
     * used, the element will be aligned horizontally using its preferred size according to the
     * {@link tripleplay.ui.Style.HAlign} binding. */
    public static final Constraint NORTH = Position.NORTH.stretched;

    /** Constraint to position an element along the bottom edge of its parent. The element is
     * stretched horizontally and uses its preferred height. If {@link Constraint#unstretched} is
     * used, the element will be aligned horizontally using its preferred size according to the
     * {@link tripleplay.ui.Style.HAlign} binding. */
    public static final Constraint SOUTH = Position.SOUTH.stretched;

    /** Constraint to position an element along the right edge of its parent. The element is
     * stretched vertically and uses its preferred width. If {@link Constraint#unstretched} is
     * used, the element will be aligned vertically using its preferred size according to the
     * {@link tripleplay.ui.Style.VAlign} binding. */
    public static final Constraint EAST = Position.EAST.stretched;

    /** Constraint to position an element along the right edge of its parent. The element is
     * stretched vertically and uses its preferred width. If {@link Constraint#unstretched} is
     * used, the element will be aligned vertically using its preferred size according to the
     * {@link tripleplay.ui.Style.VAlign} binding. */
    public static final Constraint WEST = Position.WEST.stretched;

    /**
     * Implements the constraints. Callers do not need to construct instances, but instead use the
     * declared constants and select or deselect the stretching option.
     */
    public static class Constraint extends Layout.Constraint {

        /**
         * Returns a new constraint specifying the same position as this, and with stretching.
         */
        public Constraint stretched () {
            return _pos.stretched;
        }

        /**
         * Returns a new constraint specifying the same position as this, and with no stretching.
         * The element's preferred size will be used and an appropriate alignment.
         */
        public Constraint unstretched () {
            return _pos.unstretched;
        }

        protected Constraint (Position pos, boolean stretch) {
            _pos = pos;
            _stretch = stretch;
        }

        protected Dimension adjust (IDimension pref, Rectangle boundary) {
            Dimension dim = new Dimension(pref);
            if (_stretch) {
                if ((_pos.orient & 1) != 0) {
                    dim.width = boundary.width;
                }
                if ((_pos.orient & 2) != 0) {
                    dim.height = boundary.height;
                }
            }
            dim.width = Math.min(dim.width, boundary.width);
            dim.height = Math.min(dim.height, boundary.height);
            return dim;
        }

        protected float align (float origin, float offset) {
            return _stretch ? origin : origin + offset;
        }

        protected final Position _pos;
        protected final boolean _stretch;
    }

    /** The horizontal gap between components. */
    public final float hgap;

    /** The vertical gap between components. */
    public final float vgap;

    /**
     * Constructs a new border layout with no gaps.
     */
    public BorderLayout () {
        this(0);
    }

    /**
     * Constructs a new border layout with the specified gap between components.
     */
    public BorderLayout (float gaps) {
        this(gaps, gaps);
    }

    /**
     * Constructs a new border layout with the specified horizontal and vertical gaps between
     * components.
     */
    public BorderLayout (float hgap, float vgap) {
        this.hgap = hgap;
        this.vgap = vgap;
    }

    @Override
    public Dimension computeSize (Elements<?> elems, float hintX, float hintY) {
        return new Slots(elems).computeSize(hintX, hintY);
    }

    @Override
    public void layout (Elements<?> elems, float left, float top, float width, float height) {
        Style.HAlign halign = resolveStyle(elems, Style.HALIGN);
        Style.VAlign valign = resolveStyle(elems, Style.VALIGN);
        Slots slots = new Slots(elems);
        Rectangle bounds = new Rectangle(left, top, width, height);
        slots.layoutNs(Position.NORTH, halign, bounds);
        slots.layoutNs(Position.SOUTH, halign, bounds);
        slots.layoutWe(Position.WEST, valign, bounds);
        slots.layoutWe(Position.EAST, valign, bounds);

        Position p = Position.CENTER;
        IDimension dim = slots.size(p, bounds.width, bounds.height);
        if (dim == null) {
            return;
        }

        Constraint c = slots.constraint(p);
        dim = c.adjust(dim, bounds);
        slots.setBounds(p,
            c.align(bounds.x, halign.offset(dim.width(), bounds.width)),
            c.align(bounds.y, valign.offset(dim.height(), bounds.height)), dim);
    }

    protected class Slots
    {
        final Map<Position, Element<?>> elements = new HashMap<Position, Element<?>>();

        Slots (Elements<?> elems) {
            for (Element<?> elem : elems) {
                if (!elem.isVisible()) continue;
                Position p = Position.positionOf(elem.constraint());
                if (p == null) {
                    throw new IllegalStateException(
                        "Element with a non-BorderLayout constraint: " + elem.toString());
                }
                if (elements.put(p, elem) != null) {
                    throw new IllegalStateException(
                        "Multiple elements with the same constraint: " + p);<commit_msg>Provide some information about the offending elements.<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.layout;

import java.util.HashMap;
import java.util.Map;

import pythagoras.f.Dimension;
import pythagoras.f.IDimension;
import pythagoras.f.Rectangle;
import tripleplay.ui.Element;
import tripleplay.ui.Elements;
import tripleplay.ui.Layout;
import tripleplay.ui.Style;

/**
 * Arranges up to 5 elements, one central and one on each edge. Added elements must have a
 * constraint from the class' listing (e.g. {@link BorderLayout#CENTER}), which determines the
 * position in the layout and stretching.
 *
 * <p>This is how the layout looks. Note north/south and east/west behavior is not quite symmetric
 * because east and west fit between the bottom of the north and top of the south:</p>
 *
 * <p><pre>
 *     |-----------------------------|
 *     |            north            |
 *     |-----------------------------|
 *     |      |               |      |
 *     |      |               |      |
 *     |      |               |      |
 *     | west |    center     | east |
 *     |      |               |      |
 *     |      |               |      |
 *     |-----------------------------|
 *     |            south            |
 *     |-----------------------------|
 * </pre></p>
 *
 * When an element is not stretched, it obeys the {@link tripleplay.ui.Style.HAlign} and {@link
 * tripleplay.ui.Style.VAlign} bindings.
 */
public class BorderLayout extends Layout
{
    /** Constraint to position an element in the center of its parent. The element is stretched in
     * both directions to take up available space. If {@link Constraint#unstretched} is used, the
     * element will be aligned in both directions using its preferred size and the {@link
     * tripleplay.ui.Style.HAlign} and {@link tripleplay.ui.Style.VAlign} bindings. */
    public static final Constraint CENTER = Position.CENTER.stretched;

    /** Constraint to position an element along the top edge of its parent. The element is
     * stretched horizontally and uses its preferred height. If {@link Constraint#unstretched} is
     * used, the element will be aligned horizontally using its preferred size according to the
     * {@link tripleplay.ui.Style.HAlign} binding. */
    public static final Constraint NORTH = Position.NORTH.stretched;

    /** Constraint to position an element along the bottom edge of its parent. The element is
     * stretched horizontally and uses its preferred height. If {@link Constraint#unstretched} is
     * used, the element will be aligned horizontally using its preferred size according to the
     * {@link tripleplay.ui.Style.HAlign} binding. */
    public static final Constraint SOUTH = Position.SOUTH.stretched;

    /** Constraint to position an element along the right edge of its parent. The element is
     * stretched vertically and uses its preferred width. If {@link Constraint#unstretched} is
     * used, the element will be aligned vertically using its preferred size according to the
     * {@link tripleplay.ui.Style.VAlign} binding. */
    public static final Constraint EAST = Position.EAST.stretched;

    /** Constraint to position an element along the right edge of its parent. The element is
     * stretched vertically and uses its preferred width. If {@link Constraint#unstretched} is
     * used, the element will be aligned vertically using its preferred size according to the
     * {@link tripleplay.ui.Style.VAlign} binding. */
    public static final Constraint WEST = Position.WEST.stretched;

    /**
     * Implements the constraints. Callers do not need to construct instances, but instead use the
     * declared constants and select or deselect the stretching option.
     */
    public static class Constraint extends Layout.Constraint {

        /**
         * Returns a new constraint specifying the same position as this, and with stretching.
         */
        public Constraint stretched () {
            return _pos.stretched;
        }

        /**
         * Returns a new constraint specifying the same position as this, and with no stretching.
         * The element's preferred size will be used and an appropriate alignment.
         */
        public Constraint unstretched () {
            return _pos.unstretched;
        }

        protected Constraint (Position pos, boolean stretch) {
            _pos = pos;
            _stretch = stretch;
        }

        protected Dimension adjust (IDimension pref, Rectangle boundary) {
            Dimension dim = new Dimension(pref);
            if (_stretch) {
                if ((_pos.orient & 1) != 0) {
                    dim.width = boundary.width;
                }
                if ((_pos.orient & 2) != 0) {
                    dim.height = boundary.height;
                }
            }
            dim.width = Math.min(dim.width, boundary.width);
            dim.height = Math.min(dim.height, boundary.height);
            return dim;
        }

        protected float align (float origin, float offset) {
            return _stretch ? origin : origin + offset;
        }

        protected final Position _pos;
        protected final boolean _stretch;
    }

    /** The horizontal gap between components. */
    public final float hgap;

    /** The vertical gap between components. */
    public final float vgap;

    /**
     * Constructs a new border layout with no gaps.
     */
    public BorderLayout () {
        this(0);
    }

    /**
     * Constructs a new border layout with the specified gap between components.
     */
    public BorderLayout (float gaps) {
        this(gaps, gaps);
    }

    /**
     * Constructs a new border layout with the specified horizontal and vertical gaps between
     * components.
     */
    public BorderLayout (float hgap, float vgap) {
        this.hgap = hgap;
        this.vgap = vgap;
    }

    @Override
    public Dimension computeSize (Elements<?> elems, float hintX, float hintY) {
        return new Slots(elems).computeSize(hintX, hintY);
    }

    @Override
    public void layout (Elements<?> elems, float left, float top, float width, float height) {
        Style.HAlign halign = resolveStyle(elems, Style.HALIGN);
        Style.VAlign valign = resolveStyle(elems, Style.VALIGN);
        Slots slots = new Slots(elems);
        Rectangle bounds = new Rectangle(left, top, width, height);
        slots.layoutNs(Position.NORTH, halign, bounds);
        slots.layoutNs(Position.SOUTH, halign, bounds);
        slots.layoutWe(Position.WEST, valign, bounds);
        slots.layoutWe(Position.EAST, valign, bounds);

        Position p = Position.CENTER;
        IDimension dim = slots.size(p, bounds.width, bounds.height);
        if (dim == null) {
            return;
        }

        Constraint c = slots.constraint(p);
        dim = c.adjust(dim, bounds);
        slots.setBounds(p,
            c.align(bounds.x, halign.offset(dim.width(), bounds.width)),
            c.align(bounds.y, valign.offset(dim.height(), bounds.height)), dim);
    }

    protected class Slots
    {
        final Map<Position, Element<?>> elements = new HashMap<Position, Element<?>>();

        Slots (Elements<?> elems) {
            for (Element<?> elem : elems) {
                if (!elem.isVisible()) continue;
                Position p = Position.positionOf(elem.constraint());
                if (p == null) {
                    throw new IllegalStateException(
                        "Element with a non-BorderLayout constraint: " + elem.toString());
                }
                if (elements.put(p, elem) != null) {
                    throw new IllegalStateException(
                        "Multiple elements: " + elem + " and " + elements.get(p) +
                        " with the same constraint: " + p);
{'fraction_non_alphanumeric': 0.10557756398163869, 'fraction_numerical': 0.0017606740866503176, 'mean_word_length': 2.098986749805144, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6717347', 'n_tokens_mistral': 4229, 'n_tokens_neox': 4046, 'n_words': 1460}