Mapping tooltip of different key onto a GXT BarSeries

Here’s a little idiom for when you’re using one key for the value of a stacked bar chart but want to map a different ValueProvider onto your tooltip (or label).

		//Tooltip Config - gets (f) value but if null, just return the int
	    SeriesToolTipConfig<DataPoint> config = new SeriesToolTipConfig<DataPoint>();
	    config.setLabelProvider(new SeriesLabelProvider<DataPoint>() {
	        @Override
	        public String getLabel(DataPoint item, 
	        		ValueProvider<? super DataPoint, ? extends Number> valueProvider) {
	        	String vindex = valueProvider.getPath();
	        	String findex = vindex.replace(ChartModel.CELL_VALUE, ChartModel.CELL_FORMATTED_VALUE);
	        	MapLabelProvider labelProvider = new MapLabelProvider(findex);
	        	String tooltip = labelProvider.getValue(item);
	        	return tooltip == null || tooltip.equals(_chartConstants.emptyLabel()) ? 
	        		  String.valueOf(valueProvider.getValue(item).intValue()) : tooltip;
	        }
	    });
series.setToolTipConfig(config);

By default, Sencha GXT charts only passes in the valueProvider and the data item used to render the stacked bar chart. So what if you want to provider a different value for the tooltip or label? How do you add text? Since be follow the google datatables JSON format, we use a key of “v” in a cell array to represent the numeric value used to generate the chart. We use a key “f” in the same cell array to represent the tooltip.

Here’s what our JSON looks like and here’s the resultant store (After the adapter transforms the data).

{
    "offset": 0,
    "totalCount": 1,
    "items": [
        {
            "title": "Registration Summary",
            "data": {
                "cols": [
                    {
                        "id": "Stacked bar one",
                        "label": "Hamburgers",
                        "type": "number"
                    },
                    {
                        "id": "Stacked bar two",
                        "label": "Fries",
                        "type": "number"
                    }
                ],
                "rows": [
                    {
                        "l": "McDonalds",
                        "c": [
                            {
                                "v": "5",
                                "f": "5 hamburgers sold!"
                            },
                            {
                                "v": "2",
                                "f": "2 fries sold"
                            }
                        ]
                    },
                    {
                        "l": "Burger King",
                        "c": [
                            {
                                "v": "0",
                                "f": "0 hamburgers sold :("
                            },
                            {
                                "v": "3",
                                "f": "3 fries sold"
                            }
                        ]
                    }
... /* More omitted */
                ]
            }
        }
    ]
}

And here’s the resultant store:

[
    {
        v1=2,
        label0=Hamburgers,
        v0=5,
        label1=Fries,
        id1=Stacked bar two,
        type1=number,
        id0=Stacked bar one,
        type0=number,
        f1=2 fries sold,
        l=McDonalds,
        f0=5 hamburgers sold!
    },
    {
        v1=3,
        label0=Hamburgers,
        v0=0,
        label1=Fries,
        id1=Stacked bar two,
        type1=number,
        id0=Stacked bar one,
        type0=number,
        f1=3 fries sold,
        l=9650SIP,
        f0=0 hamburgers sold 😦
    }
... /* more omitted */
]

As you can see, we use v0 – vN to as values to render per bar in our chart, where each of v0 – vN corresponds to 1 stack within the bar. Here’s the resulting tooltip:

Custom tooltip
Custom tooltip
Advertisement

Drill-down charting with Sencha GXT 3.1

My latest assignment at work was to use charting in GXT and build a widget that has the ability to render one chart and drill down into charts of greater detail when the user clicks on an object in the parent chart. To complicate matters, the data used to render the drill-down chart is variable depending on the parameters passed in through our REST API. Finally, I wanted to encapsulate all this behavior within a portlet that could be added to a portal container by the user. How did I solve this?

When I started out, I knew that there were already handlers that could be attached to a series within a chart, but as with all things Sencha, nothing is as easy as it seems at first glance. Furthermore, I was battling with Sencha’s currently buggy beta release of GXT 3.1, which led me more than once to waste several hours thinking that I was doing something wrong when in fact, there was a problem on Sencha’s end. At any rate, this is how I solved the problem:

      Step 1: Define a chart object whose store can be manipulated through a configuration

For the drill-down chart, I decided to implement my own object which takes an object called ChartConfig. My ChartConfig class is just an object containing various options on how to render the chart, including what optional parameters to pass into my REST call.

public class ChartConfig {

	public int height;
	public int width;
	public Boolean initiallyInvisible;
	public List<Color> seriesColors; //Color of series in order
	public HashMap<String, String> options;	//REST options

}

I then defined my custom chart object to take this configuration into account when rendering itself.

public class MyBarChart extends UpdateableChart implements IsWidget {	
	
	public RegistrationBarChart() {
		super();
		_model = new StackedBarChartModel();
	}
	
	public RegistrationBarChart(ChartConfig _config) {
		super(_config);
		_model = new StackedBarChartModel();
	}
	
    @Override
	public Widget asWidget() 
	{
		if (_chart == null) 
		{	
			try {
				buildChart();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return _chart;
	}
}

buildChart() is simply a method that uses the configuration, instantiates a new Chart and sets the ListStore that's used to render the chart. Don't worry about the StackedBarChartModel(). This is a custom data model and adapter that I had to implement to convert from JSON (following google's datatable format here: https://developers.google.com/chart/interactive/docs/reference#dataparam) to a ListStore consumable by Sencha (an entirely different battle. To be described in a follow-up post).

In any case, UpdateableChart is an abstract class that executes the REST API call based on the ChartConfig and regenerates the ListStore. Since I figured that all of our charts would follow the same logic here, I made it an abstract class with the subclasses needing to implement only one method: buildSeries();

public abstract class UpdateableChart {
	/**
	 * Abstract Methods
	 */
	public abstract void buildSeries();

	/**
	 * Refreshes the current chart with parameters passed in through the ChartConfig
	 * These parameters, in the HashMap<String,String> options map, get serialized
	 * and appended to the base REST URL.  
	 * 
	 * If options == NULL, the default url is used.
	 * 
	 * Upon success, the function will get a newstore and will call
	 * 	updateStore(newStore)
	 * @param c
	 */
	public void refresh(ChartConfig c) {
		String url = (c == null || c.getOptions().isEmpty()) ? _model.getListUrl() : serializeOptions(_model.getListUrl(), c.getOptions());
		
		_config = c;	//update config upon refresh
		
		RequestBuilder restReq = new RequestBuilder(RequestBuilder.GET,
				URL.encode(url)); //Use your flavor of request builder
		
		restReq.sendRequest(null, new () {

			@Override
			public void onSuccess(Response response) {
				JsonListReader<ChartObjects, ChartObject> reader = _model.getListReader();
				ListLoadResult<ChartObject> charts = reader.read(null, response.getText());
				//ListLoadResult<ChartObject> charts = reader.read(null, _resources.jsonPieData().getText());
				List<ChartObject> chartList = charts.getData();
				
				try {
					ChartObject chartObject = chartList.get(0);	//right now, only render first chart
					ListStore<DataPoint> newStore = _model.getListStore(chartObject);
					updateStore(newStore);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			@Override
			public void onFailure(RestError restError) {
				//logger.error("Rest error" + restError.getErrorMessage());
			}
		});
	}
}

In the method above, you can see that after the REST Call is made and the JSON response is parsed into a ChartObject (basically a schema defined to work with Sencha's JSONReader), I use the adapter to convert the ChartObject into a ListStore which is then used to update the store which renders the chart. Here's an example of an implementation for my bar chart of buildSeries().

	@Override
	public void buildSeries() {
		for (int x = 0; x < _chart.getSeries().size(); x++) {
			_chart.removeSeries(_chart.getSeries(x));
		}
		
		BarSeries<DataPoint> series = createStackedBar();
		_chart.addSeries(series);
		
		refreshChart();  //regenerates chart axes
		setVisible(true);  //turn visible - after REST call returns
	}

In a nutshell, the work flow is like this:

  • Create a model to represent a ListStore which is used in a chart.
  • Instantiate a chart with a dummy store, which models this ListStore
  • Implement buildSeries() which will take the new ListStore object (in the class) and use it to regenerate the series
  • Re-render the chart
  • This framework makes it so that any charts that we add to our portal are able to be updated simply by calling the “refresh(ChartConfig config)” method.

        Step Two: From your entry point, add a method that will rerender a chart based on some variable parameter

    In our portlet, we have a pie chart that contains drill-downs to bar charts depending on what slice you pick. Before binding the drill-down event to the actual pie chart, I just wanted to make sure that charts could rerender properly.

    I did this by creating an entry point to the refresh(ChartConfig config) method on my bar chart object. I added a selector to our portlet widget and bound it to a “toggleView()” method which basically just generates the ChartConfig based on the selected value and calls refresh on the correct chart. Notice that I set both charts to visible(false) so that they don’t appear to the user. When implementing the buildSeries() method for both my pie and bar charts, I actually make the chart visible after the rest call returns.

    
    private void toggleViews(SeriesSelectionEvent<DataPoint> event) {
    		displayParent = !displayParent;
    		
    		//Show Pie Chart
    		if (displayParent) {	
    			_pieChart.setVisible(false);
    			_pieChart.refresh(_pieChartConfig);
    			_barChart.setVisible(false);
    			_backButton.disable();
    		}
    		
    		//Show Bar Chart (Drill Down)
    		else {
    			_pieChart.setVisible(false);
    			_barChart.setVisible(false);  //set invisible until rest call loads, buildSeries() will make the chart visible again
    			
    			//refresh registration bar chart depending on what slice is selected
    			_barChartConfig = _barChart.getChartConfig();
    			_barChartConfig.setOption(ChartConfig.TYPE, event.getItem().get(ChartModel.COL_LABEL));
    			_barChartConfig.setOption(ChartConfig.FILTER, "registrationState(EQ)"+event.getItem().get(ChartModel.COL_LABEL));
    			_barChartConfig.setOption(ChartConfig.LIMIT, "10");
    			_barChartConfig.setOption(ChartConfig.ORDER_BY, "count");
    			_barChartConfig.setOption(ChartConfig.ORDER_DIR, "DESC");
    			
    			if (_barChartConfig.getOption(ChartConfig.TYPE).equalsIgnoreCase(pc.registered())) {
    			   List<Color> c = new ArrayList<Color> ();
    			   c.add(new RGB(148, 174, 10));
    			   _barChartConfig.setSeriesColors(c);
    			} else if (_barChartConfig.getOption(ChartConfig.TYPE).equalsIgnoreCase(pc.unregistered())) {
    			   List<Color> c = new ArrayList<Color> ();
    			   c.add(new RGB(17, 95, 166));
    			   _barChartConfig.setSeriesColors(c);
    			} else if (_barChartConfig.getOption(ChartConfig.TYPE).equalsIgnoreCase(pc.partially_registered())) {
    			   List<Color> c = new ArrayList<Color> ();
    			   c.add(new RGB(166, 17, 32));
    			   _barChartConfig.setSeriesColors(c);
    			}
    			_barChart.refresh(_barChartConfig);
    			_backButton.enable();
    		}
    	}
    

    Finally, the last step was to bind the handler to the actual slices of my pie chart. One thing to keep in mind here. Because the handler is bound to the series of the pie chart, I couldn’t rebuild the pie series otherwise the event handler would actually fall off. Thus, I knew that after the first time the series was built, I needed to bind the handler, then leave it up to the ListStore to regenerate the pie chart. To do this, when I instantiate the pie chart, I had to set an empty ListStore to the store to render the pie chart. I’ve also included my buildSeries implementation for the pie chart below so you can see that I don’t rebuild the series at all with the pie chart.

    public class MyPieChart extends UpdateableChart implements IsWidget {
    	
    	private PieSeries<DataPoint> _series;
    	
        @Override
    	public Widget asWidget() 
    	{
    		if (_chart == null) 
    		{	
    			try {
    				buildChart();
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			
    	    	//refresh(_config);	//refresh the chart store	
    		}
    
    		return _chart;
    	}
    	
    	/**
    	 * 	Iterates over each series and add a line series for each
    	 *  This is done only once.
    	 * @throws Exception 
    	 */
        @Override
    	public void buildChart() throws Exception {
    		_chart = new Chart<DataPoint>();
    		_chart.setDefaultInsets(10);
    		_store = _model.getBlankStore(numSeries, 1);   //This is where I bind the blank store to the chart
    		_chart.bindStore(_store);
    		_chart.setShadowChart(false);
    		_chart.setAnimated(true);		
    		_chart.setVisible(!_config.getInitiallyInvisible());
    		
    	    _series = createPieSeries();   //Note that this is only created once.
    	    _series.addColor(slice1);
    	    _series.addColor(slice2);
    	    _series.addColor(slice3);
    		_chart.addSeries(_series);
    		
    		final Legend<DataPoint> legend = new Legend<DataPoint>();
    		legend.setPosition(Position.RIGHT);
    		legend.setItemHighlighting(true);
    		legend.setItemHiding(true);
    		legend.getBorderConfig().setStrokeWidth(0);
    		_chart.setLegend(legend);
    		
    
    	}
     
            @Override
    	public void buildSeries() {
    		//Only build the Series the first time so we don't lose the selection handler binding
    		if (_store.size() > 0)
    			setVisible(true);
    	}
    }
    
        Step 3: Bind the drill down event handler to the series of the parent chart

    In the portlet widget, which as described above has both a pieChart and a barChart, after instantiating the pieChart, I bound the SeriesSelectionHandler to the pieChart in order to enable the drilldown. I didn’t have to wait for the REST call return to have data either, because I created a PieSeries with my dummy ListStore. This part I’m not that proud of. Because I bind the SeriesSelectionHandler outside the actual MyPieChart object, the implementation violates encapsulation. I could have implemented the SeriesSelectionHandler directly in MyPieChart, but I didn’t want to do this because I felt it would confuse developers later on why a PieChart had a BarChart object in it. I thought it would also set a bad precedent for development because it would seem like we needed to nest objects within objects whenever we wanted to create a drilldown. My implementation, though perhaps not ideal, increased our flexibility because it allows me to create PieCharts that don’t necessarily have to have barChart objects in them and yet still maintain the drilldown capability. In the future, we could create a datamodel that contains a tree-like hierarchy for parent/child drilldowns, where the back button always rerendered the parent, and the drilldown always rendered the child clicked. Something to think about. In any case, here is the binding of the toggleView() method to the SeriesSelectionEvent.

    	@Override
    	public Widget asWidget() {
    _pieChart.addSeriesHandlers(new SeriesSelectionHandler<DataPoint> () {
    
    				@Override
    				public void onSeriesSelection(
    						SeriesSelectionEvent<DataPoint> event) {
    					toggleViews(event);
    				}
    				
    			});
    }
    

    That’s it for now! Let me know if you have any questions or comments. In a post to come, I’ll describe how I modeled the google datatables JSON format into a ChartObject and how it’s consumed by GXT charts.

    The end result:

    The top level pie chart
    The top level pie chart

    Drilling down by clicking on a pie slice.
    Drilling down by clicking on a pie slice.

    Node.js vs Vert.x (Part 1)

    Now for a change of pace. Recently at work, we’ve been trying to figure out what platform to build an application that will handle serving realtime data to customers. We want the system to be fast, scalable and able to handle hundreds, maybe thousands or even tens of thousands of requests per second.

    I did a bit of prototyping in both node and vert.x to see how they performed under pressure. To do this, I built a cute webapp that makes a bunch of requests on basic web servers written in both node.js and vert.x to see how fast they could respond and how they would handle under a heavy load of requests. Here’s a picture of the ui made for the webapp (build in angular.js).

    Node webapp
    Node webapp

    I created a form that allows for various inputs. In it, one can specify several variables including the following:

    The variables:

    • Iterations – number of http requests to make
    • Block Size – How often a result is computed (reports start time for request, end time, average time per call (ms) and total time (ms) )
    • Range – How many results to display on screen – the graph tracks this
    • Polling – Toggle On/Off (this will start polling requests as fast as node.js can handle them. These are serial in nature).
    • Show Graph – toggle graphing on/off (off will provide better javascript performance)

    Thanks to angular-seed for the fast prototyping and angular-google-chart for charting.

    Benchmarking Parameters: Each request is a simple get request made to the respective webserver, which then writes a header and a “Hello” response. The requests are made through angular’s $http method. When a successful response is received, the callback function calls another $http request, until the number of successful responses received equals the number of iterations specified. I measure the time it takes from the time the request is made until the number of requests received per block is complete.

    Time Keeping: I try to avoid all delays that could be attributable to javascript rendering (e.g. the timestamp is taken when the first request in the block is made. Then the timestamp is recorded when the # of responses in a block is received. I send both these parameters to a javascript function, which is responsible for rendering the results to the display. I also added a function to enable polling requests to be made, which makes $http requests as fast as responses can be received in order to add stress to the server’s load. This is enabled with the “polling” button.

    Here’s a snippet of the webserver source code.

    In Node.js:

    StaticServlet.prototype.handleRequest = function(req, res) {
      var self = this;
    
    
    
      var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
        return String.fromCharCode(parseInt(hex, 16));
      });
    
      console.log(path);
      if (path == './show') {
    
          res.writeHead(200, {'content-type': 'text/html'});
          res.write("Hello: ");
          res.end();
    
      } else if (path == './version') {
        res.writeHead(200, {'content-type': 'text/html'});
          res.write("Node.js version: " + 'v0.10.26');
          res.end();
        
      } else {
        var parts = path.split('/');
        if (parts[parts.length-1].charAt(0) === '.')
          return self.sendForbidden_(req, res, path);
        else {
          fs.stat(path, function(err, stat) {
            if (err)
              return self.sendMissing_(req, res, path);
            if (stat.isDirectory())
              return self.sendDirectory_(req, res, path);
            return self.sendFile_(req, res, path);
          });
        }
      }
    }
    

    In Vert.x:

    
    server.requestHandler(function(req) {
    var file = '';
    
      if (req.path() == '/show') {
      	  req.response.chunked(true);
      	  req.response.putHeader('content-type','text/html');
          req.response.write("Hello: ");
          req.response.end();
      } else if (req.path() == '/version') {
      	  req.response.chunked(true);
      	  req.response.putHeader('content-type','text/html');
          req.response.write('Vertx version: ' + '2.0.2-final (built 2013-10-08 10:55:59');
          req.response.end();
      } else if (req.path() == '/') {
        file = 'index.html';
        req.response.sendFile('app/' + file);   
      } else if (req.path().indexOf('..') == -1) {
        file = req.path();
        req.response.sendFile('app/' + file);   
      }
      
    

    See? Dead simple. Of course there are lots of flaws with this methodology (e.g. the webservers are only serving static data, are writing short responses, are not optimized, etc.). It wasn’t my intention to come to a hard conclusion with this. I just wanted a data point and to experiment with both platforms. It turns out they (at least in this test) came very close to one another in terms of performance. Both servers were running on my machine, which specs are listed below.

    System Specs: Macbook Pro, mid-2012, 2.3ghz with 16gb ram and a 512gb ssd. Both webservers are running locally on my machine with a bunch of other apps open.

    And here are some preliminary results:

    Here’s the node.js webserver, with polling turned on:

    Node while polling, 1000 requests, 50 request block size
    Node while polling, 1000 requests, 50 request block size

    Here’s the vert.x webserver, with polling turned on:

    Vert.x while polling, 1000 requests, 50 request block size
    Vert.x while polling, 1000 requests, 50 request block size

    You can see that they’re very close. Next I tried stressing both servers a bit through running several concurrent queries and several “instances” of the web app. In a later post, I’ll put up some more detailed results with trying to stress both webservers out. The response time definitely slows down as more are being made.

    Conclusions: Both webservers are surprisingly close in terms of response/processing/overhead time. My CPU usage goes a bit higher on the vert.x server, but I do have several other applications running. I also haven’t tested running multiple instances of the same verticle yet in vert.x, or trying to fork processes in node.js. Both webservers are as barebones as they get. So in other words, I can’t make any hard conclusions yet, except to say that both servers are

    • Able to handle high loads of requests per second (probably on the order of 500 – 1000
    • Out of the box, both servers run roughly equivalently

    These results seemed a little bit surprising to me, given that on the web vert.x seems to have faster results. One factor that may contribute to this is the lack of realism in server response. It’s probably not the case that so many requests would be coming in simultaneously (or there would be multiple server instances to handle such requests), and the response size is very small. Since both servers are basically just writing to a stream, as long as the I/O is written with performance in mind, this may be roughly as fast as a my CPU can handle writing responses. Another hypothesis is perhaps that vert.x really shines in handling multiple instances. I’ll have to experiment and report my results.

    Postscript: In case you want to try it out for yourself, I’ve made the source code available on my github @ https://github.com/rocketegg/Code-Projects/tree/master/ServerPerformance I know this test has been done with much more sophistication and by a lot of people out there, but hopefully you can play around with this webapp, have fun and learn something.

    Custom Theming with Sencha themebuilder: Sencha GXT Theme Builder HTML

    In Sencha’s GXT 3.1 beta, it includes an html file that runs a script that can parse a theme file and generate the approximate look and appearance of several sencha widgets. This allows for fast iteration of .theme development (and enables you to not have to rebuild the theme.jar file with every change). It definitely is extremely valuable in terms of saving time, however, the tool is far from perfect. It’s located in ../gxt-3.1.0-beta-20140225/themebuilder/bin/widgetdatasample/sliceme/index.html. You’ll have to extract the files from the .jar first.

    Screen Shot 2014-02-28 at 10.56.52 AM

    An example rendering using the html form included with the themebuilder.
    An example rendering using the html form included with the themebuilder.

    A few of the deficiencies:

    • It didn’t work in chrome
    • Not every widget gets rendered
    • You can’t see widgets within widgets
    • You’ll need to modify the reset.css if you want to see custom styling
    • A lot of the widgets don’t have text, so you can’t see what text would look like in them
    • It’s difficult to determine what the widgets map to since they are prefaced with the name of the job that the themebuilder uses to “slice” the look into images for cross-browser support for older browsers.

    Here’s a few tips and tricks I’ve learned:

    • It didn’t work in chrome
      • Solution: It did work in firefox for me.
    • Not every widget gets rendered
      • Solution: Not much you can do here. Hopefully you can get to a point where your .theme is close enough so you can narrow down the widgets that you need to see to fix.
    • You can’t see widgets within widgets
      • Solution: Same as the above.
    • You can’t add custom styling because the .theme file doesn’t support it. If your app uses custom styles on top of the theme.jar, you won’t be able to see them.
      • Solution: There is a reset.css file in the same directory as the HTML file that is used to help style the rendered page. You can add .css classes here and using debug tools or firebug, insert your css class into the widget to see how it would look. You can do the same thing with external fonts.
      • See an example here:

        An example of modifying inline styles to get the look and feel right with compiled widgets.
        An example of modifying inline styles to get the look and feel right with compiled widgets.
    • A lot of the widgets don’t have text, so you can’t see what text would look like in theme
      • Solution: Same as the above. Except modify the inline HTML and add some text inside the innermost div.
      • An example of modifying the inline text of the widget using firebug.
        An example of modifying the inline text of the widget using firebug.
    • It’s difficult to determine what the widgets map to since they are prefaced with the name of the job that the themebuilder uses to “slice” the look into images for cross-browser support for older browsers.
      • Solution: This is kind of guess and check, but here’s a list of some of the less obvious mappings from the .theme file to what they correspond to in terms of gwt widgets:
      • In your .theme file,
      • 
            /* Dialog boxes - background
             */
            window {
              backgroundColor = theme.bgColor
              borderColor = util.darkenColor(backgroundColor, 0.2)
              headerGradient = util.gradientString(theme.headerGradientDetails)
            }
        
           /*
            * These are the overlays that disable fields.  Since they're grey, change the font to white.
            */
            mask {
              backgroundColor = "#000000"
              opacity = 0.5
              box {
                backgroundColor = "#303030"
                borderColor = "#555555"
                borderRadius = 0
                borderStyle = "solid"
                borderWidth = 2
                loadingImagePosition = "0 center"
                padding = util.padding(5)
                radiusMinusBorderWidth = util.max(0, borderRadius - borderWidth)
                text = theme.riverbedBodyWhite
                textPadding = util.padding(0, 0, 0, 21)
              }
            }
        
            //Dropdowns menus 
            fieldset {
              ...
              border=util.border('solid', '#bbbbbb', 1)
              text=theme.text  //Color of the unselected values in a dropdown menu
            }
        
            field {
                text=theme.text  //Color of the selected value in a dropdown in a toolbar
            }
        
        tabs {
              ...
              lastStopColor="#000000"  //color of the bottom of the tab
              tabHeight=20 //height of a tab
              tabSpacing=1 //horizontal spacing between the tabs
        
            }
        

    GWT/GXT Theming Part 3 – Adding Custom Fonts

    Okay, a softball for today. In order to add custom fonts to your application on top of Sencha’s themebuilder, you can take a few different approaches.

    Yay different fonts!
    Yay different fonts!
    1. You can add the font type to an external css file and pass in the font-family into your .theme file
    2. You can add custom appearance classes for each widget to get a particular font. See here.

    I ultimately settled on the first approach because it saved me the time of having to create a separate appearance class for each widget I wanted to style. That and Sencha’s .theme file actually has a “text” value for most of its widgets. It’s probably one of the most customizable aspects of the themebuilder.

    Here’s how I did it:

    theme {
    
      name = "customTheme"
      basePackage = "com.example"
    
      ...
    
      // CUSTOM FONTS
      /* TRADE GOTHIC/ARIAL */
        customSmallFont = util.fontStyle("\'TradeGothicW01-BoldCn20 675334\', Arial, Helvetica, sans-serif", "13px", "#000000", "normal")
    }
    

    You can see here that I escaped the single quotes for a font-family that contained spaces. This font string gets compiled through and actually makes it out to the ultimate .css that styles various widgets. Prior to using single escaped quotes, I just entered in the entire string in double quotes, which compiled and built a theme, but did not make it through to the .css in the end.

    If your font doesn’t have spaces, you can simply use the entire string, but escaping with single quotes seems to work well. Here’s what my external .css file looks like at the end:

    /* my external css file */
    @font-face {
        font-family: 'TradeGothicW01-BoldCn20 675334';
        src: url('fonts/tradegothicltpro-bdcn20-webfont.eot');
        src: url('fonts/tradegothicltpro-bdcn20-webfont.eot?#iefix') format('embedded-opentype'),
             url('fonts/tradegothicltpro-bdcn20-webfont.woff') format('woff'),
             url('fonts/tradegothicltpro-bdcn20-webfont.ttf') format('truetype'),
             url('fonts/tradegothicltpro-bdcn20-webfont.svg#TradeGothicW01-BoldCn20 675334') format('svg');
        font-weight: normal;
        font-style: normal;
    
    }
    
    

    That’s all there is to it!