More on TabLayoutPanel

Okay, in the interest of moving on with my life, I am going to call the last couple days of GWT experiments to be over. But first! I am publishing a couple of code sketches of what I’ve been working on.

I think I have gotten the hang of working with TabLayoutPanel. (see my last post for lessons learned). Below are two demos of a financial app that I sketched out.

  1. The first demo has the TabLayoutPanel working.
  2. The second demo I have added icons to the TabLayoutPanel tabs, as well as adding click handlers to the buttons on the InfoBar and the “About” link in the header.

(Note: above doesn’t work on IE8. Not sure why, and I haven’t been testing on IE at all… well, these are just experiments to learn GWT, so, sorry about that. Moving on…)

I’m really not a designer, so forgive the ugly colors and css. But it does work, and it is pretty brilliant that thanks to GWT this will work across browser (although if you look at the demos in IE you will see that my header image isn’t working right… that’s ok, I was focused on the Tab layout in this experiment.)

Below are the UiBinder template I used for the tabs, and also the css:

<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
	xmlns:g="urn:import:com.google.gwt.user.client.ui"
	xmlns:trade="urn:import:org.filsa.gwt.tradedash.client.ui"
	>

	<ui:image field="rateTabLogo" src="chart_24x24.png"></ui:image>
	<ui:image field="listTabLogo" src="coins_24x24.png"></ui:image>
	<ui:image field="marketTabLogo" src="yen_24x24.png"></ui:image>

	<ui:style>
		.rateboardPanel {
			background: #efefef;
			border-right: 1px solid #cdcdcd;
			border-bottom: 1px solid #cdcdcd;
		}
		.tabCard {
			border: solid 3px #8d8d8d;
			height: 100%;
		}

		.tab { height: 100%;
  		    cursor: pointer;
		    text-shadow: rgba(255, 255, 255, 1) 0 1px 1px;
		    font-size: 1.2em;
		    font-weight: bold;
		    color: #000;
		    border-top: 1px solid #666;
		    border-left: 1px solid #999;
		    border-right: 1px solid #666;

		    vertical-align: middle;

		}

		@sprite .rateTab {gwt-image: "rateTabLogo";}
		@sprite .listTab {gwt-image: "listTabLogo";}
		@sprite .marketTab {gwt-image: "marketTabLogo";}

		.left {float: left;}

	</ui:style>
	<g:HTMLPanel addStyleNames="{style.rateboardPanel}">
		<g:TabLayoutPanel barHeight="40" barUnit="PX" ui:field="tabPanel">
		<g:tab>
			<g:header>
				<div class="{style.tab}">
					<span class="{style.rateTab} {style.left}"></span>
					<span>Rateboard</span>
				</div>
			</g:header>
			<g:Label addStyleNames="{style.tabCard}">RateBoard here.</g:Label>
		</g:tab>
		<g:tab>
			<g:header>
				<div class="{style.tab}">
					<span class="{style.listTab} {style.left}"></span>
					<span>RateList</span>
				</div>
			</g:header>
			<trade:RateListCard  addStyleNames="{style.tabCard}"></trade:RateListCard>
		</g:tab>
		<g:tab>
			<g:header>
				<div class="{style.tab}">
					<span class="{style.marketTab} {style.left}"></span>
					<span>MarketBoard</span>
				</div>
			</g:header>
			<g:Label addStyleNames="{style.tabCard}">MarketBoard here here.</g:Label>
		</g:tab>

		</g:TabLayoutPanel>

	</g:HTMLPanel>
</ui:UiBinder>

Note the highlighted span tags above. I used one span for the image, and one for the title of the tab. If there is a better way to do this I’m open to suggestions. Please leave a comment!

And the css I used for the gwt-TabLayoutPanel:

.gwt-TabLayoutPanel
{
	height: 100%;
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabs
{}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTab
{
		background: #a4a4a4;
		margin: 0px 5px;
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTab-selected
{
		background: #8d8d8d;
}
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTabInner
{
}

The height:100% is what makes the tab contents appear, otherwise the tab content area doesn’t show at all. Something to do with absolute positioning in CSS, from what I understand.

Hope this helps, and let me know if you have better suggestions.

GWT Notes: TabLayoutPanel

I’ve been putting in some time over the past week building UI’s with GoogleWebToolkit and the new UIBinder.

It is all very spiffy and so much easier to use than 1.7 was. Last summer I tried to put together some moderately complex UI’s and found that doing everything in Java to be slow and tedious.

I will cautiously say that I am getting the hang of UIBinder, and that is far and away my current favorite feature from the new 2.0.0 release.

That said, there are a few minor snags.

The new LayoutPanels are for fast, browser-rendered widgets, done in standards mode. I prefer them to the old Panels they replace because they don’t use tables, instead the layout is all clean css and <div>s.

But when I started working with the TabLayoutPanel, I ran into some problems which I’ve listed below.

The TabLayoutPanel doesn’t display at all, or you get a weird collapsed layout (if you’ve added css). Remember, you  must use standards mode <!DOCTYPE html> for LayoutPanels, and you have to use RootLayoutPanel.get() instead of the old RootPanel.get().

There aren’t any default css styles. If all you see are the tabs titles with no styling… check the apidoc to see the css styles you need to add. This isn’t actually a bug, but without default css the class is pretty much worthless. (There is an issue for this.)
The tabs display, but the tab widgets (content area) don’t. You need to set the  height of the .gwt-TabLayoutPanel.
It doesn’ work in IE (what does?).  There’s a bug with TabLayoutPanel and IE, and a simple css workaround:
.gwt-TabLayoutPanel .gwt-TabLayoutPanelTab { float: left; }

Update: See my next post, More on TabLayoutPanel for some sample css and demos.

Update 2: GWT version 2.0.1 address the problems with IE and provides a default CSS style. You still have to remember to use RootLayoutPanel, and standards mode doctype, when using a LayoutPanel.

Whoa, StackOverflow error

I totally cracked myself up at work yesterday. I managed to run some code that threw a StackOverflow error.

It was something along the lines of:


public String getSender() { return this.getSender(); }

I had copy-and-pasted some class template code from elsewhere in the (huge, sprawling) code base. Which contained this error, but was not ever called in the that location, so it never showed up. (So it’s not my fault really, but I should have caught it. Oh wait, I did!)

Oh, the dangers of copy and paste.

Code Sketch: System Tray in Java

As I mentioned a week or so ago, this year I am going to release whatever I am working on, on a regular basis. The idea is that even if I fail or release something that is half finished, I can learn more by turning it out to the wild than if I never share it with anyone.

These bits of code are not big enough to be called apps, and don’t have enough explanatory text to call them demos or tutorials (although I want to do that as well). I’ll call them…. code sketches!

The first one I worked on over the New Year break. It’s code for a simple system tray, done in Java. The code is available from scn, and the license is GPL.

Java allows access to native System Tray notifications.

You can check it out here, on the code sketch page: /code/sketches/system-tray

ConcurrentModificationError Mystery

So, today I got bit by the ConcurrentModificationError. I had some code which had a bit of a bad smell but was working fine. The class is a batch process, that processes a request. Based on the request, a particular subset of accounts are processed. The method below gets the target accounts based on the request, and additionally filters out corporate accounts. And the ConcurrentModificationError happens on line 9:

	/**
	 * Get accounts to process based on BatchRequest, filtering out corporate accounts.
	 * @param request
	 */
	public void getAccounts (BatchRequest request) {

		List<Account> accounts = AccountManager.getAccounts(request);

		for (Account oneAccount : accounts) {
			if (isCorporateAccount(oneAccounts)) {
				accounts.remove(oneAccount);
			}
		}

	}

The List which the AccountManager is passing back has a fail-fast iterator, which detects that it is being modified in line 11, and thus failing. The mystery is–this has been working in production for months. Today, on UAT we tested a second batch job with very similar data (for all intents and purposes it is completely the same request, just a different group of accounts) and on the second pass only, the ConcurrentModificationError gets thrown. The first pass through it is fine.

Color me confused. It should work both times, or fail both times. And yes, the data is basically the same–I triple checked that today. But it works the first time, and fails the second.

Rather than spend more time wondering why it works once and fails the second time, I figure I should just eliminate the possibility of the ConcurrentModificationException entirely.  I had to make changes to the AccountManager module, which I hadn’t had the chance to change before. But it is definitely a cleaner solution. By adding a signature to the AccountManager.getAccounts(BatchRequest request, boolean includeCorporateAccounts) I was able to simplify the code to something like this:


	private static final boolean EXCLUDE_CORPORATE_ACCTS_FLAG = false;

	/**
	 * Get accounts to process based on BatchRequest, filtering out corporate accounts.
	 * @param request
	 */
	public void getAccounts (BatchRequest request) {

		return AccountManager.getAccounts(request, EXCLUDE_CORPORATE_ACCTS_FLAG);

	}

Hello, 2010!

Well, 2009 was an eventful year for me, but not so much for the blog. I am hoping to change that in this brand new decade, with the idea that I can learn and improve faster if I am writing down and sharing what I am learning. In the last couple years the blog has mostly been a place for me to make note of a solution to a technical problem I had encountered–a backup brain of technical stuff  I found useful and was likely to forget. I’ll probably keep doing that, but also want to write some beefier articles and how-tos and especially post code I’ve written.

I definitely want to code more little projects. I’ve decided I’d rather just create some things and put them out into the world without too much worry about who they will impress or their ultimate usefulness. It’s better to ship software rather than just have big plans. Just like exercise–no one will raise an eyebrow at my glacial running pace but really, it’s for my benefit not everyone else’s. The same thinking applies to this blog as well.

For those of you who choose to read (hi mom!…. mom?) thanks for hanging with me and I hope to have something more substantial for you to read in this new year.

Cheers!