org.dashbuilder.renderer.chartjs.lib.Chart Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dashbuilder-renderer-chartjs Show documentation
Show all versions of dashbuilder-renderer-chartjs Show documentation
Dashbuilder Renderer for the Chart JS API
The newest version!
package org.dashbuilder.renderer.chartjs.lib;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.CanvasElement;
import com.google.gwt.dom.client.Document;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import org.dashbuilder.renderer.chartjs.lib.event.*;
import org.dashbuilder.renderer.chartjs.lib.options.*;
import org.dashbuilder.renderer.chartjs.lib.resources.ChartStyle;
import org.dashbuilder.renderer.chartjs.lib.resources.Resources;
import java.util.ArrayList;
import java.util.List;
/**
* Base class for all chart widgets
* Class describes generic behavior of all chart widgets
*/
public abstract class Chart extends SimplePanel implements HasAnimationCompleteHandlers, HasClickHandlers,HasAnimation, HasDataSelectionEventHandlers, IsResponsive{
private static Resources resources;
protected LegendOption options = LegendOption.get();
protected JavaScriptObject nativeCanvas;
private CanvasElement canvas;
protected ChartStyle style;
protected List callbackList = new ArrayList();
static{
resources = GWT.create(Resources.class);
}
/**
* This constructor creates new chart instance with custom {@link ChartStyle}
* @param style - new CssResource used for styling charts
*/
public Chart(ChartStyle style){
//setChartStyle(style);
registerNativeAnimationHandlers();
canvas = Document.get().createCanvasElement();
getElement().appendChild(canvas);
sinkEvents(Event.ONCLICK);
addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent clickEvent) {
JavaScriptObject obj = clickEvent.getNativeEvent().cast();
JavaScriptObject data = getClickPoints(obj, nativeCanvas);
if (data != null)
DataSelectionEvent.fire(Chart.this, Chart.this, data);
}
});
}
/**
* Constructor creates chart with default style
*/
public Chart() {
this(resources.chartStyle());
}
private native JavaScriptObject getClickPoints(JavaScriptObject event, JavaScriptObject canvas)/*-{
if(canvas == null || event == null)
return null;
try {
return canvas.getPointsAtEvent(event);
}
catch(e){
//exception occurred when added additional ClickHandler which destroys chart before processing.
return null;
}
}-*/;
/**
* Set new style to the char widget. New style will be injected automatically.
* NOTICE: new style will be applied after re-drawing of chart
* @param style
*/
public void setChartStyle(ChartStyle style){
style.ensureInjected();
setStylePrimaryName(style.chart());
}
protected void processEvents(JavaScriptObject object){
this.nativeCanvas = object;
}
@Override
protected void onAttach() {
ChartJs.ensureInjected();
super.onAttach();
draw();
}
/**
* Method re-drawing chart widget without re-requesting data from data provider.
* To update data call {@link #reload()} method instead
*/
public abstract void update();
/**
* Method requesting data from data provider and re-drawing chart.
*/
public abstract void reload();
/**
* Method preparing data and invoking native draw method
* This method should not be overridden by sub-classes
*/
protected abstract void draw();
/**
* Method sets pixel width of chart area
* @param width - width in pixels
* TODO: replace it with generic {@link #setWidth(String)} and {@link #setSize(String, String)}
*/
public void setPixelWidth(int width) {
canvas.setWidth(width);
}
public void setWidth(String width) {
canvas.getStyle().setProperty("width", width);
}
public void setHeight(String height){
canvas.getStyle().setProperty("height", height);
}
/**
* Method sets pixel height of chart area
* @param height - height in pixels
* TODO: replace it with generic {@link #setHeight(String)} and {@link #setSize(String, String)}
*/
public void setPixelHeight(int height) {
canvas.setHeight(height);
}
@Override
public void addAnimationCompleteHandler(AnimationCompleteHandler handler) {
addHandler(handler, AnimationCompleteEvent.getType());
}
/**
* Creates snapshot of current state of chart as image
* @return Image object or null if Chart not rendered (or in progress)
*/
public Image getSnapshot(){
String code= getBase64Image(nativeCanvas);
if(code == null)
return null;
Image image = new Image(code);
return image;
}
private native String getBase64Image(JavaScriptObject nativeCanvas)/*-{
if(nativeCanvas != null)
return nativeCanvas.toBase64Image();
return null;
}-*/;
@Override
/**
* Important Note : clickHandler added internally by default to handle DataSelectionEvent.
* In case external clickHandler destroying chart (eg update() method invoked) this will lead
* to DataSelectionEvent won't be created
*/
public HandlerRegistration addClickHandler(ClickHandler clickHandler) {
return addHandler(clickHandler, ClickEvent.getType());
}
@Override
public HandlerRegistration addDataSelectionHandler(DataSelectionHandler handler) {
return addHandler(handler, DataSelectionEvent.getType());
}
protected JavaScriptObject getNativeCanvas(){
return nativeCanvas;
}
protected CanvasElement getNativeElement(){
return canvas;
}
protected void setNativeCanvas(JavaScriptObject object){
this.nativeCanvas = object;
processEvents(object);
}
/**
* Specify should chart be animated or not
* Default value is true
* @param enabled
*/
public void setAnimationEnabled(boolean enabled){
if(!enabled) //"animation" : false interpreted by chart.js as "true"
options.clearProperty(ANIMATION);
else
options.setProperty(ANIMATION, enabled);
}
/**
* Specify animation easing
* Default value is {@link org.dashbuilder.renderer.chartjs.lib.options.Type#EASE_OUT_QUART}
* @param type
*/
public void setAnimationType(Type type){
if(type == null)
options.clearProperty(ANIMATION_EASING);
else
options.setProperty(ANIMATION_EASING, type.getValue());
}
/**
* Add animation callback to handle animation state changes
* @param callback
*/
public void addAnimationCallback(AnimationCallback callback){
if(callback != null)
callbackList.add(callback);
}
@Override
public void setAnimationSteps(int steps) {
if(steps <= 0)
throw new IndexOutOfBoundsException("Number of animation steps should be positive. Found '"+steps+"'");
options.setProperty(ANIMATION_STEPS, steps);
}
/**
* Method returns custom options for chart
* @return
*/
protected JavaScriptObject constructOptions(){
return options;
}
protected native void registerNativeAnimationHandlers()/*-{
options = [email protected]::constructOptions()();
self = this;
options.onAnimationProgress = function(progress){
[email protected]::onAnimationProgress(D)(progress);
return;
}
options.onAnimationComplete = function(){
[email protected]::onAnimationComplete()();
return;
}
}-*/;
protected void onAnimationProgress(double progress){
for(AnimationCallback callback : callbackList){
if(callback != null)
callback.onProgress(progress);
}
}
protected void onAnimationComplete(){
for(AnimationCallback callback : callbackList){
if(callback != null)
callback.onAnimationComplete();
}
}
@Override
public void setResponsive(boolean responsive){
if(!responsive)
options.clearProperty(RESPONSIVE);
else
options.setProperty(RESPONSIVE, true);
}
@Override
public void setMaintainAspectRatio(boolean aspectRatio){
if(!aspectRatio)
options.clearProperty(MAINTAIN_ASPECT_RATIO);
else
options.setProperty(MAINTAIN_ASPECT_RATIO, true);
}
public void setLegendTemplate(String template) {
options.setLegendTemplate(template);
}
}