package fr.lteconsulting.client;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import fr.lteconsulting.hexa.client.css.bindings.BootstrapHexaCss;
import fr.lteconsulting.hexa.client.tools.Action1;
import fr.lteconsulting.hexa.client.ui.widget.ListBox;
import fr.lteconsulting.hexa.databinding.Mode;
import fr.lteconsulting.hexa.databinding.gwt.Binder;
import fr.lteconsulting.hexa.databinding.properties.Properties;
import fr.lteconsulting.hexa.databinding.properties.PropertyChangedEvent;
import fr.lteconsulting.hexa.databinding.properties.PropertyChangedHandler;
import fr.lteconsulting.hexa.databinding.watchablecollection.Change;
import fr.lteconsulting.hexa.databinding.watchablecollection.WatchableCollection;
/**
* This widget presents the different Articles and allow to select one
*
* @author Arnaud Tournier (c) LTE Consulting - 2015 http://www.lteconsulting.fr
*
*/
public class ArticleList extends Composite {
@UiField
ListBox<Article> listBox;
@UiField
DivElement listDiv;
@UiField
Button add;
@UiField
Button delete;
Element currentActiveElement = null;
private final WatchableCollection<Article> articles = Repository.getArticles();
private static ArticleListUiBinder uiBinder = GWT.create(ArticleListUiBinder.class);
interface ArticleListUiBinder extends UiBinder<Widget, ArticleList> {
}
public ArticleList() {
initWidget(uiBinder.createAndBindUi(this));
initListBox();
initArticleList();
registerSelectArticleClickAction();
registerSelectedArticleProperty();
initAddHandler();
initDeleteHandler();
listBox.getElement().focus();
}
private void initListBox() {
/**
* Use the {@link WatchableCollection} subscription system to get
* changes happening to the list
*/
articles.addCallbackAndSendAll(new Action1<List<Change>>() {
@Override
public void exec(List<Change> param) {
for (Change c : param) {
// Each change has a type and conveys the item that was
// concerned
switch (c.getType()) {
case ADD:
listBox.addItem(c.<Article> getItem().getName(), c.<Article> getItem());
break;
case REMOVE:
listBox.removeItem(c.<Article> getItem());
break;
}
}
}
});
/**
* Bind the selected article to the list box.
*
* This is a two way data binding !
*/
Binder.bind(articles, "selected").to(listBox);
}
private void initArticleList() {
/**
* Use the {@link WatchableCollection} subscription system to get
* changes happening to the list
*/
articles.addCallbackAndSendAll(new Action1<List<Change>>() {
@Override
public void exec(List<Change> changes) {
for (Change c : changes) {
// Each change has a type and conveys the item that was
// concerned
switch (c.getType()) {
case ADD:
AnchorElement anchor = Document.get().createAnchorElement();
anchor.setHref("#");
anchor.addClassName(BootstrapHexaCss.CSS.listGroupItem());
Binder.bind(c.getItem(), "name").mode(Mode.OneWay).to(anchor, "innerText");
listDiv.appendChild(anchor);
break;
case REMOVE:
listDiv.getChild(c.getIndex()).removeFromParent();
break;
}
}
}
});
}
private void registerSelectArticleClickAction() {
addDomHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int index = DOM.getChildIndex(listDiv, event.getNativeEvent().getEventTarget().<Element> cast());
if (index < 0)
return;
/**
* Set the "selected" property of the articles list (and
* notifies others).
*
* Since the articles's class ({@link WatchableCollection} does
* not have a "selected" getter nor field, the binding mecanism
* will use the dynamic property system)
*/
Properties.setValue(articles, "selected", articles.get(index));
event.stopPropagation();
event.preventDefault();
}
}, ClickEvent.getType());
}
private void registerSelectedArticleProperty() {
/**
* Register to the "selected" property of the articles list to change
* the active state of the corresponding item.
*/
Properties.register(articles, "selected", new PropertyChangedHandler() {
@Override
public void onPropertyChanged(PropertyChangedEvent event) {
// deactivate previously selected item
if (currentActiveElement != null)
currentActiveElement.removeClassName(BootstrapHexaCss.CSS.active());
/**
* Get the "selected" property of the articles list
*
* Since the articles's class ({@link WatchableCollection} does
* not have a "selected" getter nor field, the binding mecanism
* will use the dynamic property system)
*/
Article article = Properties.getValue(articles, "selected");
if (article != null) {
int index = articles.indexOf(article);
if (index >= 0)
currentActiveElement = listDiv.getChild(index).<Element> cast();
}
// activate selected item
if (currentActiveElement != null)
currentActiveElement.addClassName(BootstrapHexaCss.CSS.active());
}
});
}
private void initAddHandler() {
add.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
/**
* Add a new article...
*/
Article newArticle = Repository.createRandomArticle();
articles.add(newArticle);
/**
* And select it with the "selected" dynamic property of the
* articles list
*/
Properties.setValue(articles, "selected", newArticle);
}
});
}
private void initDeleteHandler() {
delete.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
/**
* Retreive the selected article through the "selected" dynamic
* property of the articles list
*/
Article toRemove = Properties.getValue(articles, "selected");
if (toRemove != null) {
/**
* Remove the article from the list.
*
* Since the list is a {@link WatchableCollection}, all
* watchers will be notified
*/
articles.remove(toRemove);
/**
* Selects an article
*/
Properties.setValue(articles, "selected",
articles.isEmpty() ? null : articles.get(articles.size() - 1));
}
}
});
}
}