com.codename1.ui.Sheet Maven / Gradle / Ivy
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.ui;
import static com.codename1.ui.ComponentSelector.$;
import com.codename1.ui.ComponentSelector.ComponentClosure;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.geom.Rectangle;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.ui.layouts.BoxLayout;
import com.codename1.ui.layouts.FlowLayout;
import com.codename1.ui.layouts.LayeredLayout;
import com.codename1.ui.plaf.Border;
import com.codename1.ui.plaf.RoundRectBorder;
import com.codename1.ui.plaf.Style;
import com.codename1.ui.plaf.UIManager;
import com.codename1.ui.util.EventDispatcher;
/**
* A light-weight dialog that slides up from the bottom of the screen on mobile devices.
* Sheets include a "title" bar, with a back/close button, a title label, and a "commands container" ({@link #getCommandsContainer() })
* which allows you to insert your own custom components (usually buttons) in the upper right.
*
* Custom content should be placed inside the content pane which can be retrieved via {@link #getContentPane() }
*
* Usage
*
* The general usage is to create new Sheet instance (or subclass), then call {@link #show() }
* to make it appear over the current form. If a different sheet that is currently being displayed, then
* calling {@link #show() } will replace it.
*
* Inter-Sheet Navigation
*
* The {@link #Sheet(com.codename1.ui.Sheet, java.lang.String) } constructor can take another
* Sheet object as a parameter, which will act as a "parent" sheet ({@link #getParentSheet() }. If the parent
* sheet is not null, then {@literal this} sheet will have a "Back" button instead of a "Close" button. THe
* "Back" button will navigate back to the parent sheet.
*
* When navigating between sheets, the sheet will be resized with a smooth slide animation to the preferred
* height of the destination sheet.
*
* Example
*
* {@code
* public void start() {
if(current != null){
current.show();
return;
}
Form hi = new Form("Hi World", new BorderLayout());
Button b = new Button("Open Sheet");
b.addActionListener(e->{
new MySheet(null).show();
});
hi.add(BorderLayout.NORTH, b);
hi.show();
}
private class MySheet extends Sheet {
MySheet(Sheet parent) {
super(parent, "My Sheet");
Container cnt = getContentPane();
cnt.setLayout(BoxLayout.y());
Button gotoSheet2 = new Button("Goto Sheet 2");
gotoSheet2.addActionListener(e->{
new MySheet2(this).show(300);
});
cnt.add(gotoSheet2);
for (String t : new String[]{"Red", "Green", "Blue", "Orange"}) {
cnt.add(new Label(t));
}
}
}
private class MySheet2 extends Sheet {
MySheet2(Sheet parent) {
super(parent, "Sheet 2");
Container cnt = getContentPane();
cnt.setLayout(BoxLayout.y());
cnt.setScrollableY(true);
for (int i=0; i<2; i++) {
for (String t : new String[]{"Red", "Green", "Blue", "Orange"}) {
cnt.add(new Label(t));
}
}
}
}
* }
*
* Video Sample
*
* Screen cast of the SheetSample demo
* View source for this sample here.
* This sample can be run directly in the SampleRunner.
*
* @author shannah
* @since 7.0
*/
public class Sheet extends Container {
private final Sheet parentSheet;
private EventDispatcher closeListeners = new EventDispatcher();
private EventDispatcher backListeners = new EventDispatcher();
private Button backButton = new Button(FontImage.MATERIAL_CLOSE);
private final Label title = new Label();
private Container commandsContainer = new Container(BoxLayout.x());
private boolean allowClose = true;
private Container titleBar = BorderLayout.center(LayeredLayout.encloseIn(
BorderLayout.center(FlowLayout.encloseCenterMiddle(title)),
BorderLayout.centerEastWest(null, commandsContainer, backButton)
));
private Container contentPane = new Container(BoxLayout.y());
private static int DEFAULT_TRANSITION_DURATION=300;
/**
* The position on the screen where the sheet is displayed on phones.
* One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST. Default is {@link BorderLayout#SOUTH}.
*
* @see #setPosition(java.lang.String)
* @see #setPosition(java.lang.String, java.lang.String)
*/
private String position = BorderLayout.SOUTH;
/**
* The position on the screen where the sheet is displayed on tablets.
* One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST. Default is {@link BorderLayout#SOUTH}.
*
* @see #setPosition(java.lang.String)
* @see #setPosition(java.lang.String, java.lang.String)
*/
private String tabletPosition = position;
private ActionListener formPointerListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
Form f = getComponentForm();
if (f == null) {
return;
}
if (Display.impl.isScrollWheeling()) {
return;
}
Component cmp = f.getComponentAt(evt.getX(), evt.getY());
if (cmp == null) {
return;
}
if (Sheet.this.contains(cmp) || Sheet.this == cmp || cmp.isOwnedBy(Sheet.this)) {
// do nothing.
} else {
evt.consume();
hide(DEFAULT_TRANSITION_DURATION);
}
}
};
/**
* Creates a new sheet with the specified parent and title.
* @param parent Optional parent sheet. If non-null, then this sheet will have a "back" button instead of a "close" button. The "back" button will return to the parent sheet.
* @param title The title to display in the title bar of the sheet.
*/
public Sheet(Sheet parent, String title) {
this(parent, title, "Sheet");
}
/**
* Creates a new sheet with the specified parent and title.
* @param parent Optional parent sheet. If non-null, then this sheet will have a "back" button instead of a "close" button. The "back" button will return to the parent sheet.
* @param title The title to display in the title bar of the sheet.
* @param uiid Optional UIID for the sheet. If non-null, then the Sheet's uiid will be {@literal uiid}, the title label's UIID will be {@literal uiid + "Title"},
* the title bar's UIID will be {@literal uiid + "TitleBar"}, and the back/close button's UIID will be {@literal uiid + "BackButton"}.
*/
public Sheet(Sheet parent, String title, String uiid) {
if (parent != null) {
allowClose = parent.allowClose;
position = parent.position;
tabletPosition = parent.tabletPosition;
}
if (uiid == null) {
uiid = "Sheet";
}
$(this).addTags("Sheet");
setGrabsPointerEvents(true);
this.setUIID(uiid);
this.title.setUIID(uiid+"Title");
titleBar.setUIID(uiid+"TitleBar");
backButton.setUIID(uiid+"BackButton");
this.parentSheet = parent;
this.title.setText(title);
initUI();
updateBorderForPosition();
}
/**
* Sets whether the user is able to close this sheet. Default is true. If you set
* this value to false, then there will be no close button, and pressing outside of the sheet
* will have no effect.
*
* Child sheets will assume the settings of the parent. The back button will still work,
* but the top level sheet will not include a close button.
*
* @param allowClose True to allow user to close the sheet. False to prevent it.
* @since 7.0
*/
public void setAllowClose(boolean allowClose) {
if (allowClose != this.allowClose) {
this.allowClose = allowClose;
if (!allowClose && isInitialized()) {
form.removePointerPressedListener(formPointerListener);
} else if (allowClose && isInitialized()) {
form.addPointerPressedListener(formPointerListener);
}
if (parentSheet == null) {
backButton.setVisible(allowClose);
backButton.setEnabled(allowClose);
}
}
}
/**
* Checks whether the user is allowed to close this sheet.
* @return True if user can close the sheet.
*
*/
public boolean isAllowClose() {
return allowClose;
}
/**
* Gets the content pane of the sheet. All sheet content should be added to the content pane
* and not directly to the sheet.
* @return The content pane.
*/
public Container getContentPane() {
return contentPane;
}
/**
* Hides the back button.
*/
public void hideBackButton() {
backButton.setVisible(false);
}
/**
* Shows the back button.
*/
public void showBackButton() {
backButton.setVisible(true);
}
/**
* Gets the container that is rendered on the top right bar of the sheet. Use this
* to add buttons and other content you wish to appear in the title bar. Best not to
* overload this with too many things.
* @return
*/
public Container getCommandsContainer() {
return commandsContainer;
}
private void initUI() {
setLayout(new BorderLayout());
contentPane.setSafeArea(true);
titleBar.setSafeArea(true);
add(BorderLayout.NORTH, titleBar);
if (parentSheet != null) {
FontImage.setMaterialIcon(backButton, FontImage.MATERIAL_ARROW_BACK);
}
add(BorderLayout.CENTER, contentPane);
backButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
back(DEFAULT_TRANSITION_DURATION);
}
});
}
/**
* Shows the sheet with the default (300ms) transition duration.
* @see #show(int)
*/
public void show() {
show(DEFAULT_TRANSITION_DURATION);
}
/**
* Gets the current sheet on the current form or null if no sheet is currently being displayed.
* @return The current sheet or null.
* @since 7.0
*/
public static Sheet getCurrentSheet() {
if (CN.getCurrentForm() == null) {
return null;
}
Container cnt = CN.getCurrentForm().getFormLayeredPaneIfExists();
if (cnt == null) {
return null;
}
class Result {
Sheet found;
}
for (Component cmp : $(".Sheet", cnt)) {
if (cmp instanceof Sheet) {
return (Sheet)cmp;
}
}
return null;
}
/**
* Shows the sheet over the current form using a slide-up transition with given duration in milliseconds.
*
* If another sheet is currently being shown, then this will replace that sheet, and use an appropriate slide
* animation to adjust the size.
* @param duration The duration of the slide transition in milliseconds.
* @see #show()
*/
public void show(final int duration) {
// We need to add some margin to the title to prevent overlap with the
// back button and the commaneds.
int titleMargin = Math.max(
commandsContainer.getPreferredW() + commandsContainer.getStyle().getHorizontalMargins(),
backButton.getPreferredW() + backButton.getStyle().getHorizontalMargins()
);
// Set the padding in the content pane to match the corner radius
Style s = getStyle();
Style titleParentStyle = title.getParent().getStyle();
titleParentStyle.setMarginLeft(titleMargin);
titleParentStyle.setMarginRight(titleMargin);
Border border = s.getBorder();
if (border instanceof RoundRectBorder) {
RoundRectBorder b = (RoundRectBorder)border;
$(contentPane).setPaddingMillimeters(b.getCornerRadius());
}
// Deal with iPhoneX notch.
UIManager uim = UIManager.getInstance();
Style statusBarStyle = uim.getComponentStyle("StatusBar");
Style titleAreaStyle = uim.getComponentStyle("TitleArea");
int topPadding = statusBarStyle.getPaddingTop() + statusBarStyle.getPaddingBottom() + titleAreaStyle.getPaddingTop();
int positionInt = getPositionInt();
Rectangle displaySafeArea = new Rectangle();
Display.getInstance().getDisplaySafeArea(displaySafeArea);
int bottomPadding = s.getPaddingBottom();
int safeAreaBottomPadding = CN.getDisplayHeight() - (displaySafeArea.getY() + displaySafeArea.getHeight());
bottomPadding = bottomPadding + safeAreaBottomPadding;
if (positionInt == S || positionInt == C) {
// For Center and South position we use margin to
// prevent overlap with top notch. This looks better as overlap is only
// an edge case that occurs when the sheet is the full screen height.
$(this).setMargin(topPadding, 0 , 0, 0);
$(this).setPadding(s.getPaddingTop(), s.getPaddingRightNoRTL(), bottomPadding, s.getPaddingLeftNoRTL());
} else {
// For other cases we use padding to prevent overlap with top notch. This looks
// better as it appears that the sheet bleeds all the way to the top edge of the screen,
// but the content is not obscured by the notch.
$(this).setPadding(topPadding, s.getPaddingRightNoRTL(), bottomPadding, s.getPaddingLeftNoRTL());
}
// END Deal with iPhoneX notch
Form f = CN.getCurrentForm();
if (f.getAnimationManager().isAnimating()) {
f.getAnimationManager().flushAnimation(new Runnable() {
public void run() {
show(duration);
}
});
return;
}
if (getParent() != null) {
remove();
}
Container cnt = CN.getCurrentForm().getFormLayeredPane(Sheet.class, true);
if (!(cnt.getLayout() instanceof BorderLayout)) {
cnt.setLayout(new BorderLayout(BorderLayout.CENTER_BEHAVIOR_CENTER_ABSOLUTE));
cnt.getStyle().setBgPainter(new Painter() {
@Override
public void paint(Graphics g, Rectangle rect) {
int alph = g.getAlpha();
g.setAlpha((int)(alph * 30/100.0));
g.setColor(0x0);
g.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
g.setAlpha(alph);
}
});
cnt.revalidate();
}
if (cnt.getComponentCount() > 0) {
$(".Sheet", cnt).each(new ComponentClosure() {
@Override
public void call(Component c) {
if (c instanceof Sheet) {
Sheet s = (Sheet)c;
if (s.isAncestorSheetOf(Sheet.this) || s == Sheet.this) {
// If the sheet is an ancestor of
// ours then we don't need to fire a close event
// yet. We fire it when it is closed
// without possibility of returning
// via a back chain
return;
}
s.fireCloseEvent(false);
// Hiding this sheet may eliminate the possibility of
// its parent sheets from being shown again,
// so their close events should also be fired in this case.
Sheet sp = s.getParentSheet();
while (sp != null) {
if (sp == Sheet.this) {
break;
}
if (!sp.isAncestorSheetOf(Sheet.this)) {
sp.fireCloseEvent(false);
}
sp = sp.getParentSheet();
}
}
}
});
Component existing = cnt.getComponentAt(0);
cnt.replace(existing, this, null);
cnt.animateLayout(duration);
} else {
cnt.add(getPosition(), this);
this.setWidth(getPreferredW(cnt));
this.setHeight(getPreferredH(cnt));
this.setX(getHiddenX(cnt));
this.setY(getHiddenY(cnt));
cnt.animateLayout(duration);
}
}
/**
* Gets the position where the Sheet is to be displayed.
* One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST. Default is {@link BorderLayout#SOUTH}.
*
* @see #setPosition(java.lang.String)
* @see #setPosition(java.lang.String, java.lang.String)
*/
public String getPosition() {
if (CN.isTablet()) {
return tabletPosition;
}
return position;
}
/**
* Sets the position where the Sheet is to be displayed.
* One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST. Default is {@link BorderLayout#SOUTH}.
*
* @param position One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST.
* @see #setPosition(java.lang.String)
* @see #setPosition(java.lang.String, java.lang.String)
* @since 7.0
*/
public void setPosition(String position) {
if (CN.isTablet()) {
if (!position.equals(tabletPosition)) {
tabletPosition = position;
updateBorderForPosition();
}
} else {
if (!position.equals(this.position)) {
this.position = position;
updateBorderForPosition();
}
}
}
private void updateBorderForPosition() {
Border border = getStyle().getBorder();
if (border instanceof RoundRectBorder) {
RoundRectBorder b = (RoundRectBorder)border;
RoundRectBorder nb = RoundRectBorder.create();
nb.bezierCorners(b.isBezierCorners());
nb.bottomLeftMode(b.isBottomLeft());
nb.bottomRightMode(b.isBottomRight());
nb.topRightMode(b.isTopRight());
nb.topLeftMode(b.isTopLeft());
nb.cornerRadius(b.getCornerRadius());
nb.shadowBlur(b.getShadowBlur());
nb.shadowColor(b.getShadowColor());
nb.shadowOpacity(b.getShadowOpacity());
nb.shadowSpread(b.getShadowSpread());
nb.shadowX(b.getShadowX());
nb.shadowY(b.getShadowY());
nb.strokeColor(b.getStrokeColor());
nb.strokeOpacity(b.getStrokeOpacity());
nb.stroke(b.getStrokeThickness(), b.isStrokeMM());
b = nb;
switch (getPositionInt()) {
case C:
b.bottomRightMode(true);
b.bottomLeftMode(true);
b.topLeftMode(true);
b.topRightMode(true);
break;
case E:
b.bottomLeftMode(true);
b.topLeftMode(true);
b.topRightMode(false);
b.bottomRightMode(false);
break;
case W:
b.bottomLeftMode(false);
b.bottomRightMode(true);
b.topLeftMode(false);
b.topRightMode(true);
break;
case S:
b.topLeftMode(true);
b.topRightMode(true);
b.bottomLeftMode(false);
b.bottomRightMode(false);
break;
case N:
b.topLeftMode(false);
b.topRightMode(false);
b.bottomLeftMode(true);
b.bottomRightMode(true);
break;
}
getStyle().setBorder(b);
}
}
/**
* Sets the position where the Sheet is to be displayed.
* One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST. Default is {@link BorderLayout#SOUTH}.
*
* @param phonePosition Position to use on a phone (i.e. non-tablet). One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST.
* @param tabletPosition Position to use on a tablet and desktop. One of {@link BorderLayout#CENTER}, {@link BorderLayout#NORTH}, {@link BorderLayout#SOUTH},
* {@link BorderLayout#WEST}. {@link BorderLayout#EAST.
* @see #setPosition(java.lang.String)
* @see #setPosition(java.lang.String, java.lang.String)
* @since 7.0
*/
public void setPosition(String phonePosition, String tabletPosition) {
boolean changed = false;
if (CN.isTablet() && !tabletPosition.equals(this.tabletPosition)) {
changed = true;
} else if (!CN.isTablet() && !phonePosition.equals(position)) {
changed = true;
}
position = phonePosition;
this.tabletPosition = tabletPosition;
if (changed) {
updateBorderForPosition();
}
}
/**
* Gets X-coordinate of the sheet when it is hidden off-screen. This will be different
* depending on the position of the sheet.
* @param cnt The container in the FormLayeredPane where the sheet is to be rendered.
* @return
*/
private int getHiddenX(Container cnt) {
switch (getPositionInt()) {
case S:
case N:
return 0;
case C:
return (cnt.getWidth() - getPreferredW(cnt))/2;
case E:
return cnt.getWidth();
case W:
return -getPreferredW(cnt);
}
return 0;
}
/**
* Gets Y-coordinate of the sheet when it is hidden off-screen. This will be different
* depending on the position of the sheet.
* @param cnt The container in the FormLayeredPane where the sheet is to be rendered.
* @return
*/
private int getHiddenY(Container cnt) {
switch (getPositionInt()) {
case S:
case C:
return cnt.getHeight();
case W:
case E:
return 0;
case N:
return -getPreferredH(cnt);
}
return 0;
}
/**
* Gets the preferred width of the sheet. Will depend on where it is rendered. If position is CENTER,
* then the preferred width will be the natural preferred width of the sheet. But NORTH or SOUTH,
* the preferred width will be the full container width.
*
* @param cnt The container in the FormLayeredPane where the sheet is to be rendered.
* @return
*/
private int getPreferredW(Container cnt) {
switch (getPositionInt()) {
case N:
case S:
return cnt.getWidth();
case C:
case W:
case E:
return Math.min(getPreferredW() + (backButton.getPreferredW() + backButton.getStyle().getHorizontalMargins())* 2, cnt.getWidth()) ;
}
return getPreferredW();
}
private static final int N=0;
private static final int S=1;
private static final int E=2;
private static final int W=3;
private static final int C=4;
private int getPositionInt() {
String pos = getPosition();
if (BorderLayout.NORTH.equals(pos)) {
return N;
}
if (BorderLayout.SOUTH.equals(pos)) {
return S;
}
if (BorderLayout.EAST.equals(pos)) {
return E;
}
if (BorderLayout.WEST.equals(pos)) {
return W;
}
if (BorderLayout.CENTER.equals(pos)) {
return C;
}
return S;
}
/**
* Gets the preferred height of the sheet. Will depend on where it is rendered. If position is CENTER, NORTH, or SOUTH
* then the preferred height will be the natural preferred width of the sheet. But WEST or EAST,
* the preferred height will be the full container height.
*
* @param cnt The container in the FormLayeredPane where the sheet is to be rendered.
* @return
*/
private int getPreferredH(Container cnt) {
switch(getPositionInt()) {
case W:
case E:
return cnt.getHeight();
default:
return Math.min(getPreferredH(), cnt.getHeight());
}
}
/**
* Goes back to the parent sheet with a default (300ms) slide animation. If there
* is no parent sheet, then this will close the sheet.
* @see #back(int)
*/
public void back() {
back(DEFAULT_TRANSITION_DURATION);
}
/**
* Goes back to the parent sheet with a slide animation of given duration. If there
* is no parent sheet, then this will close the sheet.
* @param duration Duration of the slide transition in milliseconds.
*/
public void back(int duration) {
if (this.parentSheet != null) {
fireBackEvent();
this.parentSheet.show(duration);
} else {
hide(duration);
}
}
private void hide(int duration) {
final Container cnt = CN.getCurrentForm().getFormLayeredPane(Sheet.class, true);
setX(getHiddenX(cnt));
setY(getHiddenY(cnt));
cnt.animateUnlayout(duration, 255, new Runnable() {
public void run() {
Container parent = cnt.getParent();
if (parent != null && parent.getComponentForm() != null) {
cnt.remove();
parent.getComponentForm().revalidateLater();
fireCloseEvent(true);
}
}
});
}
/**
* Gets the parent sheet or null if there is none.
* @return The parent sheet or null.
*/
public Sheet getParentSheet() {
return parentSheet;
}
private Form form;
@Override
protected void initComponent() {
super.initComponent();
form = getComponentForm();
if (form != null && allowClose) {
form.addPointerPressedListener(formPointerListener);
}
}
@Override
protected void deinitialize() {
if (form != null) {
form.removePointerPressedListener(formPointerListener);
form = null;
}
super.deinitialize();
}
/**
* Finds Sheet containing this component if it is currently part of a Sheet.
* @param cmp The component to check.
* @return The sheet containing the component, or null if it is not on a sheet.
* @since 7.0
*/
public static Sheet findContainingSheet(Component cmp) {
Container parent = cmp.getParent();
while (parent != null) {
if (parent instanceof Sheet) {
return (Sheet)parent;
}
parent = parent.getParent();
}
return null;
}
/**
* Checks if the current sheet is an ancestor sheet of the given sheet.
* @param sheet The sheet to check
* @return True if the current sheet is an ancestor of sheet.
* @since 7.0
*/
public boolean isAncestorSheetOf(Sheet sheet) {
sheet = sheet.getParentSheet();
if (sheet == this) {
return true;
} else if (sheet == null) {
return false;
} else {
return isAncestorSheetOf(sheet);
}
}
/**
* Adds listener notified when the sheet is closed. This event is only fired
* when the sheet is closed without the possibility of being reopened. E.g. if a
* child sheet is opened (causing this sheet to be hidden), the close event won't be
* fired until either that child sheet is hidden (without going back),
* or the sheet itself is hidden, or goes back.
* @param l
* @since 7.0
*/
public void addCloseListener(ActionListener l) {
closeListeners.addListener(l);
}
/**
* Removes a close listener.
* @param l The close listener
*/
public void removeCloseListener(ActionListener l) {
closeListeners.removeListener(l);
}
private void fireCloseEvent(boolean parentsToo) {
closeListeners.fireActionEvent(new ActionEvent(this));
if (parentsToo && parentSheet != null) {
parentSheet.fireCloseEvent(true);
}
}
/**
* Adds listener to be notified when user goes back to the parent. This is not
* fired if the sheet is simply closed. Only if the "back" button is pressed,
*
* @param l Listener
* @since 7.0
*/
public void addBackListener(ActionListener l) {
backListeners.addListener(l);
}
/**
* Removes a back listener.
* @param l The close listener
*/
public void removeBackListener(ActionListener l) {
backListeners.removeListener(l);
}
private void fireBackEvent() {
backListeners.fireActionEvent(new ActionEvent(this));
}
}