package com.yoghurt.crypto.transactions.client.widget; import java.util.ArrayList; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Style.Visibility; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Widget; import com.yoghurt.crypto.transactions.client.resources.R; import com.yoghurt.crypto.transactions.client.util.misc.Color; public class ContextFieldSet<T> extends FlowPanel { private static final int POPUP_SHOW_DELAY = 50; private static final int POPUP_HIDE_DELAY = 70; protected ArrayList<ContextField<T>> fields = new ArrayList<ContextField<T>>(); public interface FieldContextFactory<T> { Widget getContextWidget(T part); } private final class TypedTimer extends Timer { private final ContextField<T> source; public TypedTimer(final ContextField<T> source) { this.source = source; } @Override public void run() { if (selectedField == null || selectedField == source) { displayContextPopup(source); } } } private final Timer popupHideTimer = new Timer() { @Override public void run() { clearActivity(); } }; private final MouseOverHandler mouseOverHandler = new MouseOverHandler() { @SuppressWarnings("unchecked") @Override public void onMouseOver(final MouseOverEvent event) { popupHideTimer.cancel(); if (popupShowTimer != null) { popupShowTimer.cancel(); } popupShowTimer = new TypedTimer((ContextField<T>) event.getSource()); popupShowTimer.schedule(popupPanel.isShowing() ? 0 : POPUP_SHOW_DELAY); } }; private final MouseOutHandler mouseOutHandler = new MouseOutHandler() { @Override public void onMouseOut(final MouseOutEvent event) { if (selectedField == null) { popupShowTimer.cancel(); delayedHide(); } } }; private ClickHandler mouseClickHandler = new ClickHandler() { @SuppressWarnings("unchecked") @Override public void onClick(final ClickEvent event) { if (selectedField != null) { selectedField.setSelected(false); } if (selectedField == event.getSource()) { selectedField = null; return; } selectedField = (ContextField<T>) event.getSource(); selectedField.setSelected(true); selectedField.fireEvent(new MouseOverEvent() {}); } }; protected FieldContextFactory<T> contextFactory; private final AttachedContextPanel popupPanel = new AttachedContextPanel(); private Timer popupShowTimer; private ContextField<T> selectedField; private final AttachEvent.Handler attachHandler = new AttachEvent.Handler() { @Override public void onAttachOrDetach(final AttachEvent event) { if (!event.isAttached()) { popupPanel.hide(); if (attachRegistration != null) { attachRegistration.removeHandler(); } } } }; private HandlerRegistration attachRegistration; public ContextFieldSet() {} public ContextFieldSet(final FieldContextFactory<T> contextFactory) { this.contextFactory = contextFactory; setStyleName(R.css().flex()); } public ContextFieldSet(final String text) { this(new TextContextFactory<T>(text)); } @Override public void clear() { super.clear(); fields.clear(); clearActivity(); } protected void clearActivity() { selectedField = null; popupPanel.hide(); } public void addField(final T value) { addField(value, getFieldColor(value)); } public void addField(final T value, final Color color) { addField(value, color, getFieldText(value)); } public ContextField<T> addField(final T value, final Color color, final String text) { final ContextField<T> field = createContextField(value, color, text); field.addMouseOverHandler(mouseOverHandler); field.addMouseOutHandler(mouseOutHandler); field.addClickHandler(mouseClickHandler); fields.add(field); return addField(field); } protected ContextField<T> createContextField(final T value, final Color color, final String text) { return new ContextField<T>(value, color, text); } protected Color getFieldColor(final T value) { // No-op by default return null; } protected String getFieldText(final T value) { // No-op by default return null; } protected ContextField<T> addField(final ContextField<T> field) { add(field); return field; } protected void removeField(final int counter) { fields.remove(counter); remove(counter); } private void delayedHide() { popupHideTimer.schedule(POPUP_HIDE_DELAY); } // TODO delegate context styling out of here private void displayContextPopup(final ContextField<T> field) { if (contextFactory == null) { return; } // Create a new content widget final Widget popupContent = contextFactory.getContextWidget(field.getValue()); final ContextPanel panel = new ContextPanel(); panel.setWidget(popupContent); final Color borderColor = getFieldColor(field.getValue()).copy(); final Color backgroundColor = borderColor.copy(); borderColor.setA(0.8); backgroundColor.setA(0.01); panel.getElement().getStyle().setBorderColor(borderColor.getValue()); popupContent.getElement().getStyle().setBackgroundColor(backgroundColor.getValue()); // Notify display displayContextForValue(field.getValue()); // Display the popup displayContextPopup(field, panel); } protected void displayContextForValue(final T value) { // No-op by default } private void displayContextPopup(final Widget target, final Widget popupContent) { if (!popupPanel.isShowing()) { popupContent.getElement().getStyle().setVisibility(Visibility.HIDDEN); } popupPanel.setWidget(popupContent); popupPanel.show(); attachRegistration = target.addAttachHandler(attachHandler); // Defer the attach event because we don't have the element's width/height at this point Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { popupPanel.attachToWidget(target); popupContent.getElement().getStyle().clearVisibility(); } }); } public void setMouseClickHandler(final ClickHandler mouseClickHandler) { this.mouseClickHandler = mouseClickHandler; } }