Path: blob/master/src/java.desktop/share/classes/java/awt/FlowLayout.java
41152 views
/*1* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt;2627import java.io.IOException;28import java.io.ObjectInputStream;29import java.io.Serial;3031/**32* A flow layout arranges components in a directional flow, much33* like lines of text in a paragraph. The flow direction is34* determined by the container's {@code componentOrientation}35* property and may be one of two values:36* <ul>37* <li>{@code ComponentOrientation.LEFT_TO_RIGHT}38* <li>{@code ComponentOrientation.RIGHT_TO_LEFT}39* </ul>40* Flow layouts are typically used41* to arrange buttons in a panel. It arranges buttons42* horizontally until no more buttons fit on the same line.43* The line alignment is determined by the {@code align}44* property. The possible values are:45* <ul>46* <li>{@link #LEFT LEFT}47* <li>{@link #RIGHT RIGHT}48* <li>{@link #CENTER CENTER}49* <li>{@link #LEADING LEADING}50* <li>{@link #TRAILING TRAILING}51* </ul>52* <p>53* For example, the following picture shows an applet using the flow54* layout manager (its default layout manager) to position three buttons:55* <p>56* <img src="doc-files/FlowLayout-1.gif"57* ALT="Graphic of Layout for Three Buttons"58* style="margin: 7px 10px;">59* <p>60* Here is the code for this applet:61*62* <hr><blockquote><pre>63* import java.awt.*;64* import java.applet.Applet;65*66* public class myButtons extends Applet {67* Button button1, button2, button3;68* public void init() {69* button1 = new Button("Ok");70* button2 = new Button("Open");71* button3 = new Button("Close");72* add(button1);73* add(button2);74* add(button3);75* }76* }77* </pre></blockquote><hr>78* <p>79* A flow layout lets each component assume its natural (preferred) size.80*81* @author Arthur van Hoff82* @author Sami Shaio83* @since 1.084* @see ComponentOrientation85*/86public class FlowLayout implements LayoutManager, java.io.Serializable {8788/**89* This value indicates that each row of components90* should be left-justified.91*/92public static final int LEFT = 0;9394/**95* This value indicates that each row of components96* should be centered.97*/98public static final int CENTER = 1;99100/**101* This value indicates that each row of components102* should be right-justified.103*/104public static final int RIGHT = 2;105106/**107* This value indicates that each row of components108* should be justified to the leading edge of the container's109* orientation, for example, to the left in left-to-right orientations.110*111* @see java.awt.Component#getComponentOrientation112* @see java.awt.ComponentOrientation113* @since 1.2114*/115public static final int LEADING = 3;116117/**118* This value indicates that each row of components119* should be justified to the trailing edge of the container's120* orientation, for example, to the right in left-to-right orientations.121*122* @see java.awt.Component#getComponentOrientation123* @see java.awt.ComponentOrientation124* @since 1.2125*/126public static final int TRAILING = 4;127128/**129* {@code align} is the property that determines130* how each row distributes empty space.131* It can be one of the following values:132* <ul>133* <li>{@code LEFT}134* <li>{@code RIGHT}135* <li>{@code CENTER}136* </ul>137*138* @serial139* @see #getAlignment140* @see #setAlignment141*/142int align; // This is for 1.1 serialization compatibility143144/**145* {@code newAlign} is the property that determines146* how each row distributes empty space for the Java 2 platform,147* v1.2 and greater.148* It can be one of the following three values:149* <ul>150* <li>{@code LEFT}151* <li>{@code RIGHT}152* <li>{@code CENTER}153* <li>{@code LEADING}154* <li>{@code TRAILING}155* </ul>156*157* @serial158* @since 1.2159* @see #getAlignment160* @see #setAlignment161*/162int newAlign; // This is the one we actually use163164/**165* The flow layout manager allows a separation of166* components with gaps. The horizontal gap will167* specify the space between components and between168* the components and the borders of the169* {@code Container}.170*171* @serial172* @see #getHgap()173* @see #setHgap(int)174*/175int hgap;176177/**178* The flow layout manager allows a separation of179* components with gaps. The vertical gap will180* specify the space between rows and between the181* the rows and the borders of the {@code Container}.182*183* @serial184* @see #getHgap()185* @see #setHgap(int)186*/187int vgap;188189/**190* If true, components will be aligned on their baseline.191*/192private boolean alignOnBaseline;193194/**195* Use serialVersionUID from JDK 1.1 for interoperability.196*/197@Serial198private static final long serialVersionUID = -7262534875583282631L;199200/**201* Constructs a new {@code FlowLayout} with a centered alignment and a202* default 5-unit horizontal and vertical gap.203*/204public FlowLayout() {205this(CENTER, 5, 5);206}207208/**209* Constructs a new {@code FlowLayout} with the specified210* alignment and a default 5-unit horizontal and vertical gap.211* The value of the alignment argument must be one of212* {@code FlowLayout.LEFT}, {@code FlowLayout.RIGHT},213* {@code FlowLayout.CENTER}, {@code FlowLayout.LEADING},214* or {@code FlowLayout.TRAILING}.215* @param align the alignment value216*/217public FlowLayout(int align) {218this(align, 5, 5);219}220221/**222* Creates a new flow layout manager with the indicated alignment223* and the indicated horizontal and vertical gaps.224* <p>225* The value of the alignment argument must be one of226* {@code FlowLayout.LEFT}, {@code FlowLayout.RIGHT},227* {@code FlowLayout.CENTER}, {@code FlowLayout.LEADING},228* or {@code FlowLayout.TRAILING}.229* @param align the alignment value230* @param hgap the horizontal gap between components231* and between the components and the232* borders of the {@code Container}233* @param vgap the vertical gap between components234* and between the components and the235* borders of the {@code Container}236*/237public FlowLayout(int align, int hgap, int vgap) {238this.hgap = hgap;239this.vgap = vgap;240setAlignment(align);241}242243/**244* Gets the alignment for this layout.245* Possible values are {@code FlowLayout.LEFT},246* {@code FlowLayout.RIGHT}, {@code FlowLayout.CENTER},247* {@code FlowLayout.LEADING},248* or {@code FlowLayout.TRAILING}.249* @return the alignment value for this layout250* @see java.awt.FlowLayout#setAlignment251* @since 1.1252*/253public int getAlignment() {254return newAlign;255}256257/**258* Sets the alignment for this layout.259* Possible values are260* <ul>261* <li>{@code FlowLayout.LEFT}262* <li>{@code FlowLayout.RIGHT}263* <li>{@code FlowLayout.CENTER}264* <li>{@code FlowLayout.LEADING}265* <li>{@code FlowLayout.TRAILING}266* </ul>267* @param align one of the alignment values shown above268* @see #getAlignment()269* @since 1.1270*/271public void setAlignment(int align) {272this.newAlign = align;273274// this.align is used only for serialization compatibility,275// so set it to a value compatible with the 1.1 version276// of the class277278switch (align) {279case LEADING:280this.align = LEFT;281break;282case TRAILING:283this.align = RIGHT;284break;285default:286this.align = align;287break;288}289}290291/**292* Gets the horizontal gap between components293* and between the components and the borders294* of the {@code Container}295*296* @return the horizontal gap between components297* and between the components and the borders298* of the {@code Container}299* @see java.awt.FlowLayout#setHgap300* @since 1.1301*/302public int getHgap() {303return hgap;304}305306/**307* Sets the horizontal gap between components and308* between the components and the borders of the309* {@code Container}.310*311* @param hgap the horizontal gap between components312* and between the components and the borders313* of the {@code Container}314* @see java.awt.FlowLayout#getHgap315* @since 1.1316*/317public void setHgap(int hgap) {318this.hgap = hgap;319}320321/**322* Gets the vertical gap between components and323* between the components and the borders of the324* {@code Container}.325*326* @return the vertical gap between components327* and between the components and the borders328* of the {@code Container}329* @see java.awt.FlowLayout#setVgap330* @since 1.1331*/332public int getVgap() {333return vgap;334}335336/**337* Sets the vertical gap between components and between338* the components and the borders of the {@code Container}.339*340* @param vgap the vertical gap between components341* and between the components and the borders342* of the {@code Container}343* @see java.awt.FlowLayout#getVgap344* @since 1.1345*/346public void setVgap(int vgap) {347this.vgap = vgap;348}349350/**351* Sets whether or not components should be vertically aligned along their352* baseline. Components that do not have a baseline will be centered.353* The default is false.354*355* @param alignOnBaseline whether or not components should be356* vertically aligned on their baseline357* @since 1.6358*/359public void setAlignOnBaseline(boolean alignOnBaseline) {360this.alignOnBaseline = alignOnBaseline;361}362363/**364* Returns true if components are to be vertically aligned along365* their baseline. The default is false.366*367* @return true if components are to be vertically aligned along368* their baseline369* @since 1.6370*/371public boolean getAlignOnBaseline() {372return alignOnBaseline;373}374375/**376* Adds the specified component to the layout.377* Not used by this class.378* @param name the name of the component379* @param comp the component to be added380*/381public void addLayoutComponent(String name, Component comp) {382}383384/**385* Removes the specified component from the layout.386* Not used by this class.387* @param comp the component to remove388* @see java.awt.Container#removeAll389*/390public void removeLayoutComponent(Component comp) {391}392393/**394* Returns the preferred dimensions for this layout given the395* <i>visible</i> components in the specified target container.396*397* @param target the container that needs to be laid out398* @return the preferred dimensions to lay out the399* subcomponents of the specified container400* @see Container401* @see #minimumLayoutSize402* @see java.awt.Container#getPreferredSize403*/404public Dimension preferredLayoutSize(Container target) {405synchronized (target.getTreeLock()) {406Dimension dim = new Dimension(0, 0);407int nmembers = target.getComponentCount();408boolean firstVisibleComponent = true;409boolean useBaseline = getAlignOnBaseline();410int maxAscent = 0;411int maxDescent = 0;412413for (int i = 0 ; i < nmembers ; i++) {414Component m = target.getComponent(i);415if (m.isVisible()) {416Dimension d = m.getPreferredSize();417dim.height = Math.max(dim.height, d.height);418if (firstVisibleComponent) {419firstVisibleComponent = false;420} else {421dim.width += hgap;422}423dim.width += d.width;424if (useBaseline) {425int baseline = m.getBaseline(d.width, d.height);426if (baseline >= 0) {427maxAscent = Math.max(maxAscent, baseline);428maxDescent = Math.max(maxDescent, d.height - baseline);429}430}431}432}433if (useBaseline) {434dim.height = Math.max(maxAscent + maxDescent, dim.height);435}436Insets insets = target.getInsets();437dim.width += insets.left + insets.right + hgap*2;438dim.height += insets.top + insets.bottom + vgap*2;439return dim;440}441}442443/**444* Returns the minimum dimensions needed to layout the <i>visible</i>445* components contained in the specified target container.446* @param target the container that needs to be laid out447* @return the minimum dimensions to lay out the448* subcomponents of the specified container449* @see #preferredLayoutSize450* @see java.awt.Container451* @see java.awt.Container#doLayout452*/453public Dimension minimumLayoutSize(Container target) {454synchronized (target.getTreeLock()) {455boolean useBaseline = getAlignOnBaseline();456Dimension dim = new Dimension(0, 0);457int nmembers = target.getComponentCount();458int maxAscent = 0;459int maxDescent = 0;460boolean firstVisibleComponent = true;461462for (int i = 0 ; i < nmembers ; i++) {463Component m = target.getComponent(i);464if (m.visible) {465Dimension d = m.getMinimumSize();466dim.height = Math.max(dim.height, d.height);467if (firstVisibleComponent) {468firstVisibleComponent = false;469} else {470dim.width += hgap;471}472dim.width += d.width;473if (useBaseline) {474int baseline = m.getBaseline(d.width, d.height);475if (baseline >= 0) {476maxAscent = Math.max(maxAscent, baseline);477maxDescent = Math.max(maxDescent,478dim.height - baseline);479}480}481}482}483484if (useBaseline) {485dim.height = Math.max(maxAscent + maxDescent, dim.height);486}487488Insets insets = target.getInsets();489dim.width += insets.left + insets.right + hgap*2;490dim.height += insets.top + insets.bottom + vgap*2;491return dim;492493494495496497}498}499500/**501* Centers the elements in the specified row, if there is any slack.502* @param target the component which needs to be moved503* @param x the x coordinate504* @param y the y coordinate505* @param width the width dimensions506* @param height the height dimensions507* @param rowStart the beginning of the row508* @param rowEnd the ending of the row509* @param useBaseline Whether or not to align on baseline.510* @param ascent Ascent for the components. This is only valid if511* useBaseline is true.512* @param descent Ascent for the components. This is only valid if513* useBaseline is true.514* @return actual row height515*/516private int moveComponents(Container target, int x, int y, int width, int height,517int rowStart, int rowEnd, boolean ltr,518boolean useBaseline, int[] ascent,519int[] descent) {520switch (newAlign) {521case LEFT:522x += ltr ? 0 : width;523break;524case CENTER:525x += width / 2;526break;527case RIGHT:528x += ltr ? width : 0;529break;530case LEADING:531break;532case TRAILING:533x += width;534break;535}536int maxAscent = 0;537int nonbaselineHeight = 0;538int baselineOffset = 0;539if (useBaseline) {540int maxDescent = 0;541for (int i = rowStart ; i < rowEnd ; i++) {542Component m = target.getComponent(i);543if (m.visible) {544if (ascent[i] >= 0) {545maxAscent = Math.max(maxAscent, ascent[i]);546maxDescent = Math.max(maxDescent, descent[i]);547}548else {549nonbaselineHeight = Math.max(m.getHeight(),550nonbaselineHeight);551}552}553}554height = Math.max(maxAscent + maxDescent, nonbaselineHeight);555baselineOffset = (height - maxAscent - maxDescent) / 2;556}557for (int i = rowStart ; i < rowEnd ; i++) {558Component m = target.getComponent(i);559if (m.isVisible()) {560int cy;561if (useBaseline && ascent[i] >= 0) {562cy = y + baselineOffset + maxAscent - ascent[i];563}564else {565cy = y + (height - m.height) / 2;566}567if (ltr) {568m.setLocation(x, cy);569} else {570m.setLocation(target.width - x - m.width, cy);571}572x += m.width + hgap;573}574}575return height;576}577578/**579* Lays out the container. This method lets each580* <i>visible</i> component take581* its preferred size by reshaping the components in the582* target container in order to satisfy the alignment of583* this {@code FlowLayout} object.584*585* @param target the specified component being laid out586* @see Container587* @see java.awt.Container#doLayout588*/589public void layoutContainer(Container target) {590synchronized (target.getTreeLock()) {591Insets insets = target.getInsets();592int maxwidth = target.width - (insets.left + insets.right + hgap*2);593int nmembers = target.getComponentCount();594int x = 0, y = insets.top + vgap;595int rowh = 0, start = 0;596597boolean ltr = target.getComponentOrientation().isLeftToRight();598599boolean useBaseline = getAlignOnBaseline();600int[] ascent = null;601int[] descent = null;602603if (useBaseline) {604ascent = new int[nmembers];605descent = new int[nmembers];606}607608for (int i = 0 ; i < nmembers ; i++) {609Component m = target.getComponent(i);610if (m.isVisible()) {611Dimension d = m.getPreferredSize();612m.setSize(d.width, d.height);613614if (useBaseline) {615int baseline = m.getBaseline(d.width, d.height);616if (baseline >= 0) {617ascent[i] = baseline;618descent[i] = d.height - baseline;619}620else {621ascent[i] = -1;622}623}624if ((x == 0) || ((x + d.width) <= maxwidth)) {625if (x > 0) {626x += hgap;627}628x += d.width;629rowh = Math.max(rowh, d.height);630} else {631rowh = moveComponents(target, insets.left + hgap, y,632maxwidth - x, rowh, start, i, ltr,633useBaseline, ascent, descent);634x = d.width;635y += vgap + rowh;636rowh = d.height;637start = i;638}639}640}641moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh,642start, nmembers, ltr, useBaseline, ascent, descent);643}644}645646//647// the internal serial version which says which version was written648// - 0 (default) for versions before the Java 2 platform, v1.2649// - 1 for version >= Java 2 platform v1.2, which includes "newAlign" field650//651private static final int currentSerialVersion = 1;652/**653* This represent the {@code currentSerialVersion}654* which is bein used. It will be one of two values:655* {@code 0} versions before Java 2 platform v1.2,656* {@code 1} versions after Java 2 platform v1.2.657*658* @serial659* @since 1.2660*/661private int serialVersionOnStream = currentSerialVersion;662663/**664* Reads this object out of a serialization stream, handling665* objects written by older versions of the class that didn't contain all666* of the fields we use now..667*668* @param stream the {@code ObjectInputStream} to read669* @throws ClassNotFoundException if the class of a serialized object could670* not be found671* @throws IOException if an I/O error occurs672*/673@Serial674private void readObject(ObjectInputStream stream)675throws IOException, ClassNotFoundException676{677stream.defaultReadObject();678679if (serialVersionOnStream < 1) {680// "newAlign" field wasn't present, so use the old "align" field.681setAlignment(this.align);682}683serialVersionOnStream = currentSerialVersion;684}685686/**687* Returns a string representation of this {@code FlowLayout}688* object and its values.689* @return a string representation of this layout690*/691public String toString() {692String str = "";693switch (align) {694case LEFT: str = ",align=left"; break;695case CENTER: str = ",align=center"; break;696case RIGHT: str = ",align=right"; break;697case LEADING: str = ",align=leading"; break;698case TRAILING: str = ",align=trailing"; break;699}700return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + str + "]";701}702703704}705706707