package org.vaadin.touchkit.ui;
import org.vaadin.touchkit.gwt.client.vcom.popover.PopoverRpc;
import org.vaadin.touchkit.gwt.client.vcom.popover.PopoverState;
import com.vaadin.ui.Component;
import com.vaadin.ui.ComponentContainer;
import com.vaadin.v7.ui.VerticalLayout;
import com.vaadin.ui.Window;
/**
* The Popover is a modal sub window suitable for mobile devices. It is often
* used to quickly display more options or small form related to an action.
* Popover does not support dragging or resizing by the end user.
* <p>
* An example of a typical use case would be the following: In a mobile mail
* application when you hit an icon, you'll see a {@link Popover} with actions:
* "Reply", "Forward" and "Print". E.g. on the iPad the actions would be shown
* next to the touched icon so they are close to the users finger (the related
* button can be set with {@link #showRelativeTo(Component)}), while on smaller
* screens (e.g. the iPhone) this kind of UI element fills the whole width of
* the screen.
* <p>
* A Popover can also be made full screen with {@link #setSizeFull()}. In this
* case, all borders will be hidden and the window will block all other content
* of the main window. Using a full screen subwindow, instead of changing the
* whole content of the main window may cause a slightly faster return to the
* original view.
* <p>
* Finally, on a pad-sized device, if the window is not full screen nor related
* to a component, it will be completely undecorated by default, allowing the
* content to dictate the look.
*/
public class Popover extends Window {
private PopoverRpc rpc = new PopoverRpc() {
@Override
public void close() {
Popover.this.close();
}
};
/**
* Constructs a new Popover. By default, the Popover is modal. This is
* usually the most sensible approach on devices.
*/
public Popover() {
setContent(new VerticalLayout());
setModal(true);
registerRpc(rpc);
}
@Override
public void beforeClientResponse(boolean initial) {
if (getWidth() == 100 && getWidthUnits() == Unit.PERCENTAGE
&& getHeight() == 100 && getHeightUnits() == Unit.PERCENTAGE) {
getState().setFullscreen(true);
}
super.beforeClientResponse(initial);
}
/**
* Constructs a new Popover with the given content.
*
* @param content
* the content for the popover.
*/
public Popover(ComponentContainer content) {
this();
setContent(content);
}
@Override
public PopoverState getState() {
return (PopoverState) super.getState();
}
/**
* This method will add the Popover to the top level window so that it is
* aligned below or on top of the given component, <em>unless</em> this is a
* small screen device, e.g a smart phone. By default, an arrow pointing to
* the given related component will be shown.
* <p>
* On a small screen device, a 100% wide overlay will slide in from the
* bottom or top depending on the given related components position (in an
* attempt to leave the related component visible, though this is not
* guaranteed).
*
* @param relatedComponent
* the component next to which the popover is shown.
*/
public void showRelativeTo(Component relatedComponent) {
getState().setRelatedComponent(relatedComponent);
if (relatedComponent != null && getParent() == null) {
relatedComponent.getUI().addWindow(this);
}
markAsDirty();
}
/**
* Sets whether the Popover is automatically closed when the user clicks
* (taps) outside of it. Note that no close button is displayed by default,
* so some other way to close the window must be arranged if set to
* <code>false</code>.
*
* @param closable
* true to allow the user to close the Popover by tapping outside
* of it
*/
@Override
public void setClosable(boolean closable) {
super.setClosable(closable);
}
/**
* Removes the popover from the UI.
*/
public void removeFromParent() {
if (getParent() != null) {
getUI().removeWindow(this);
}
}
/**
* A convenience method that adds a component to the popover's content
* container.
*
* @param c
* the component to add.
*/
public void addComponent(Component c) {
Component content2 = getContent();
if (content2 instanceof ComponentContainer) {
ComponentContainer contentPanel = (ComponentContainer) content2;
contentPanel.addComponent(c);
} else {
throw new RuntimeException(
"Content component is not of type ComponentContainer");
}
}
}