/*
* Copyright (c) 2011 PonySDK
* Owners:
* Luciano Broussal <luciano.broussal AT gmail.com>
* Mathieu Barbier <mathieu.barbier AT gmail.com>
* Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com>
*
* WebSite:
* http://code.google.com/p/pony-sdk/
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.ponysdk.core.ui.basic;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.json.JsonArray;
import javax.json.JsonNumber;
import javax.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ponysdk.core.model.ClientToServerModel;
import com.ponysdk.core.model.HandlerModel;
import com.ponysdk.core.model.ServerToClientModel;
import com.ponysdk.core.model.WidgetType;
import com.ponysdk.core.ui.basic.event.HasPAnimation;
import com.ponysdk.core.ui.basic.event.PCloseEvent;
import com.ponysdk.core.ui.basic.event.PCloseHandler;
import com.ponysdk.core.writer.ModelWriter;
/**
* A panel that can "pop up" over other widgets. It overlays the browser's
* client area (and any previously-created popups).
* <p>
* A PPopupPanel should not generally be added to other panels; rather, it
* should be shown and hidden using the {@link #show()} and {@link #hide()}
* methods.
* </p>
* <p>
* The width and height of the PPopupPanel cannot be explicitly set; they are
* determined by the PPopupPanel's widget. Calls to {@link #setWidth(String)}
* and {@link #setHeight(String)} will call these methods on the PPopupPanel's
* widget.
* </p>
* <p>
* The PopupPanel can be optionally displayed with a "glass" element behind it,
* which is commonly used to gray out the widgets behind it. It can be enabled
* using {@link #setGlassEnabled(boolean)} . It has a default style name of
* "gwt-PopupPanelGlass", which can be changed using
* {@link #setGlassStyleName(String)}.
* </p>
* <h3>CSS Style Rules</h3>
* <dl>
* <dt>.gwt-PopupPanel</dt>
* <dd>the outside of the popup</dd>
* <dt>.gwt-PopupPanel .popupContent</dt>
* <dd>the wrapper around the content</dd>
* <dt>.gwt-PopupPanelGlass</dt>
* <dd>the glass background behind the popup</dd>
* </dl>
*/
public class PPopupPanel extends PSimplePanel implements HasPAnimation {
private static final Logger log = LoggerFactory.getLogger(PPopupPanel.class);
private final boolean autoHide;
private final List<PCloseHandler> listeners = new ArrayList<>();
private boolean animationEnabled;
private boolean showing;
private PPositionCallback positionCallback;
protected PPopupPanel(final boolean autoHide) {
this.visible = false;
this.autoHide = autoHide;
}
protected PPopupPanel() {
this(false);
}
@Override
protected void enrichOnInit(final ModelWriter writer) {
super.enrichOnInit(writer);
if (autoHide) writer.write(ServerToClientModel.POPUP_AUTO_HIDE, autoHide);
}
@Override
protected WidgetType getWidgetType() {
return WidgetType.POPUP_PANEL;
}
public void setModal(final boolean modal) {
saveUpdate(ServerToClientModel.POPUP_MODAL, modal);
}
public void setDraggable(final boolean draggable) {
saveUpdate(ServerToClientModel.POPUP_DRAGGABLE, draggable);
}
public void center() {
this.showing = true;
saveUpdate(writer -> writer.write(ServerToClientModel.POPUP_CENTER));
}
public void show() {
if (!showing) {
this.showing = true;
saveUpdate(writer -> writer.write(ServerToClientModel.POPUP_SHOW));
} else {
log.warn("The popup is already opened : {}", this);
}
}
public void hide() {
if (showing) {
this.showing = false;
saveUpdate(writer -> writer.write(ServerToClientModel.POPUP_HIDE));
} else {
log.warn("The popup is already hidden : {}", this);
}
}
public void close() {
hide();
removeFromParent();
}
@Override
public boolean isAnimationEnabled() {
return animationEnabled;
}
@Override
public void setAnimationEnabled(final boolean animationEnabled) {
if (Objects.equals(this.animationEnabled, animationEnabled)) return;
this.animationEnabled = animationEnabled;
saveUpdate(ServerToClientModel.ANIMATION, animationEnabled);
}
public void setGlassEnabled(final boolean glassEnabled) {
saveUpdate(ServerToClientModel.POPUP_GLASS_ENABLED, glassEnabled);
}
public boolean isShowing() {
return showing;
}
public void setPopupPosition(final int left, final int top) {
saveUpdate(writer -> {
writer.write(ServerToClientModel.POPUP_POSITION_LEFT, left);
writer.write(ServerToClientModel.POPUP_POSITION_TOP, top);
});
}
public void setPopupPositionAndShow(final PPositionCallback callback) {
this.positionCallback = callback;
this.showing = true;
saveAddHandler(HandlerModel.HANDLER_POPUP_POSITION);
}
public void addCloseHandler(final PCloseHandler handler) {
listeners.add(handler);
}
@Override
public void onClientData(final JsonObject instruction) {
if (instruction.containsKey(ClientToServerModel.POPUP_POSITION.toStringValue())) {
final JsonArray widgetInfo = instruction.getJsonArray(ClientToServerModel.POPUP_POSITION.toStringValue());
int i = 0;
final int windowWidth = ((JsonNumber) widgetInfo.get(i++)).intValue();
final int windowHeight = ((JsonNumber) widgetInfo.get(i++)).intValue();
final int clientWith = ((JsonNumber) widgetInfo.get(i++)).intValue();
final int clientHeight = ((JsonNumber) widgetInfo.get(i++)).intValue();
setPosition(windowWidth, windowHeight, clientWith, clientHeight);
saveUpdate(writer -> writer.write(ServerToClientModel.POPUP_POSITION_AND_SHOW));
} else if (instruction.containsKey(ClientToServerModel.HANDLER_CLOSE.toStringValue())) {
this.showing = false;
listeners.forEach(handler -> handler.onClose(new PCloseEvent(this)));
} else {
super.onClientData(instruction);
}
}
public void setPosition(final int offsetWidth, final int offsetHeight, final int windowWidth, final int windowHeight) {
this.positionCallback.setPosition(offsetWidth, offsetHeight, windowWidth, windowHeight);
this.visible = false;
setVisible(true);
}
public void setGlassStyleName(final String glassStyleName) {
saveUpdate(ServerToClientModel.POPUP_GLASS_STYLE_NAME, glassStyleName);
}
/**
* A callback that is used to set the position of a {@link PPopupPanel}
* right before it is shown.
*/
public interface PPositionCallback {
void setPosition(int offsetWidth, int offsetHeight, int windowWidth, int windowHeight);
}
}