/*
* Copyright 2014-2015 CyberVision, Inc.
*
* 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 org.kaaproject.avro.ui.gwt.client.widget;
import org.kaaproject.avro.ui.gwt.client.AvroUiResources;
import org.kaaproject.avro.ui.gwt.client.AvroUiResources.AvroUiStyle;
import org.kaaproject.avro.ui.gwt.client.util.Utils;
import org.kaaproject.avro.ui.shared.FormField;
import org.kaaproject.avro.ui.shared.FormField.ChangeListener;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class FieldWidgetPanel extends Composite implements HasValue<Boolean>, ClickHandler, ChangeListener {
interface FieldWidgetPanelUiBinder extends UiBinder<Widget, FieldWidgetPanel> { }
private static FieldWidgetPanelUiBinder uiBinder = GWT.create(FieldWidgetPanelUiBinder.class);
private static final int OPEN_ANIMATION_DURATION = 350;
private static ContentAnimation contentAnimation;
@UiField public FlowPanel fieldSet;
@UiField public Label legendLabel;
@UiField public Label legendNotesLabel;
@UiField public Image arrowImage;
@UiField public CheckBox legendBox;
@UiField public SimplePanel legendPanel;
@UiField public SimplePanel contentPanel;
@UiField (provided=true) public final AvroUiResources avroUiResources;
@UiField (provided=true) public final AvroUiStyle avroUiStyle;
/** Signals whether the fieldset is opened. */
private boolean isOpen;
private FormField field;
public FieldWidgetPanel(AvroUiStyle style, FormField field, boolean readOnly, boolean openByDefault) {
this.field = field;
avroUiResources = Utils.resources;
avroUiStyle = style;
initWidget(uiBinder.createAndBindUi(this));
contentPanel.getElement().getStyle().setProperty("padding", "0px");
contentPanel.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
legendLabel.setText(field.getDisplayName());
if (!field.isOptional()) {
legendLabel.addStyleName(avroUiStyle.requiredField());
}
if (field.isOverride()) {
arrowImage.setVisible(false);
legendBox.setVisible(true);
legendBox.setEnabled(!readOnly && !field.isReadOnly());
if (!readOnly && !field.isReadOnly()) {
legendBox.addClickHandler(this);
field.addChangeListener(this);
}
setOpen(field.isChanged(), false);
} else {
legendBox.setVisible(false);
arrowImage.setVisible(true);
arrowImage.addClickHandler(this);
setOpen(openByDefault, false);
}
setContentDisplay(false);
}
@Override
public void onChanged(boolean changed) {
setValue(changed, false, true);
}
public void setContent(Widget widget) {
contentPanel.setWidget(widget);
setContentDisplay(false);
}
public Widget getContent() {
return contentPanel.getWidget();
}
public void setLegendWidget(Widget widget) {
legendPanel.setVisible(true);
legendPanel.setWidget(widget);
}
public void setLegendNotes(String notes) {
legendNotesLabel.setVisible(true);
legendNotesLabel.setText(notes);
}
@Override
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<Boolean> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
@Override
public Boolean getValue() {
return isOpen;
}
@Override
public void setValue(Boolean value) {
setValue(value, false);
}
@Override
public void setValue(Boolean value, boolean fireEvents) {
setValue(value, fireEvents, false);
}
public void setValue(final Boolean value, final boolean fireEvents, boolean animate) {
if (value == this.isOpen) {
return;
}
Boolean before = this.isOpen;
field.setChanged(value, fireEvents);
setOpen(value, animate);
if (fireEvents) {
ValueChangeEvent.fireIfNotEqual(this, before, value);
}
}
public boolean isOpen() {
return isOpen;
}
public void setOpen(boolean isOpen, boolean animate) {
if (this.isOpen != isOpen) {
this.isOpen = isOpen;
if (field.isOverride()) {
legendBox.setValue(isOpen);
}
setContentDisplay(animate);
}
}
@Override
public void onClick(ClickEvent event) {
setValue(!isOpen, true, true);
}
private void setContentDisplay(boolean animate) {
if (contentAnimation == null) {
contentAnimation = new ContentAnimation();
}
contentAnimation.setOpen(this, animate);
}
private void updateStyles() {
if (isOpen) {
fieldSet.removeStyleName(avroUiStyle.fieldsetInvisible());
fieldSet.addStyleName(avroUiStyle.fieldsetVisible());
arrowImage.setResource(avroUiResources.arrowBottomImage());
} else {
fieldSet.removeStyleName(avroUiStyle.fieldsetVisible());
fieldSet.addStyleName(avroUiStyle.fieldsetInvisible());
arrowImage.setResource(avroUiResources.arrowRightImage());
}
}
/**
* An {@link Animation} used to open the content.
*/
private static class ContentAnimation extends Animation {
/**
* Whether the item is being opened or closed.
*/
private boolean opening;
/**
* The {@link DisclosurePanel} being affected.
*/
private FieldWidgetPanel curPanel;
/**
* Open or close the content.
*
* @param panel the panel to open or close
* @param animate true to animate, false to open instantly
*/
public void setOpen(FieldWidgetPanel panel, boolean animate) {
// Immediately complete previous open
cancel();
// Open the new item
if (animate) {
curPanel = panel;
opening = panel.isOpen;
run(OPEN_ANIMATION_DURATION);
} else {
panel.updateStyles();
panel.contentPanel.setVisible(panel.isOpen);
panel.contentPanel.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
if (panel.isOpen) {
panel.contentPanel.getElement().getStyle().clearProperty("overflow");
if (panel.getContent() != null) {
panel.getContent().setVisible(true);
}
} else {
panel.contentPanel.getElement().getStyle().setProperty("overflow", "hidden");
}
}
}
@Override
protected void onComplete() {
if (!opening) {
curPanel.contentPanel.setVisible(false);
curPanel.updateStyles();
} else {
curPanel.contentPanel.getElement().getStyle().clearProperty("overflow");
curPanel.contentPanel.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
}
curPanel.contentPanel.getElement().getStyle().setProperty("height", "auto");
curPanel = null;
}
@Override
protected void onStart() {
super.onStart();
if (opening) {
curPanel.updateStyles();
curPanel.contentPanel.setVisible(true);
curPanel.contentPanel.getElement().getStyle().setDisplay(Display.INLINE_BLOCK);
if (curPanel.getContent() != null) {
curPanel.getContent().setVisible(true);
}
} else {
curPanel.contentPanel.getElement().getStyle().setProperty("overflow", "hidden");
}
}
@Override
protected void onUpdate(double progress) {
int scrollHeight = curPanel.contentPanel.getElement().getPropertyInt("scrollHeight");
int height = (int) (progress * scrollHeight);
if (!opening) {
height = scrollHeight - height;
}
height = Math.max(height, 1);
curPanel.contentPanel.getElement().getStyle().setProperty("height", height + "px");
}
}
}