Why design patterns from web development makes crappy iOS or Android apps.

Increasingly I’ve been encountering software developed for iOS which was written by someone who cut their eye-teeth on writing UI software for the web in Javascript. And I’ve noticed a common pattern which arises from that code which makes their code overly complex and hard to maintain on iOS.

Basically, they treat UIView-based objects as passive objects.

And if you started live writing software on the web using Javascript, this makes sense. After all, the view structure represented in the DOM hierarchy is by and large a passive participant in the user interface. When you want to present a list of items in a scrolling region, for example, you would create the scrolling region, then populate the items in that region, setting the attributes of each of the <div> sections and <li> sections with the appropriate CSS styles for overflow and font information.

(In something like GWT it’s not hard to create your own custom view objects, just as you can create custom view objects in Javascript. But at the bottom of the stack, the Menu class or the Table class you built simply declares a <div> and manipulates the subset of the DOM within that <div> tag. The view hierarchy is still a passive participant; you simply wrap part of that passive content into something with some logic.)


The problem is when you move all this onto iOS or onto Android or onto Windows or onto MacOS X, you lose one of the most powerful elements of those platforms–and that is Views are active objects rather than passive participants.

Meaning that a child class of a UITableViewCell in iOS can actively know how to present a data record passed to it; you do not need to put the code to populate the table view cell within the UITableViewController class. You can build an Image View which knows how make a network call and load and cache an image without putting image loading and caching code into the UIViewController. You can create a single view which has complex behavior–without having to put the behavior code inside the view controller.

And this allows you to create very complex user interface elements and reuse them throughout your code.

Of course this also needs to be balanced with the available tools. For example, it makes no sense to create a custom UIButton which presents a button with a different font, when you can set the font and appearance of a default button within the NIB.

But it does indicate that in many cases the proper thing to do is to push functionality down into the view, rather than make the view controller responsible for that logic.


You can’t do this in Javascript. You cannot create a new <myTableTag> which takes special arguments and presents the contents in a unique way, which can be reused throughout your site.

And there is nothing inherently “wrong” with putting all the logic for your button’s behavior, the way table view cells are laid out, and the responsibility for loading images in the background into your view controller.

It just makes life far more complex than it has to be, because you’re leaving one of the most important tools in your arsenal on the floor.

Dragging and dropping objects in GWT

If you want to add a click-and-drag handler in GWT, so that (for example) if you click on an image object, you can move it around (and drag content logically associated with it), it’s fairly straight forward.

First, you need to implement a MouseDownEvent, a MosueMoveEvent and a MouseUpEvent handler, and attach them to your image. (Me, I like putting this into a single class, which contains the state associated with the dragging event.) Thus:

	Image myImage = ...
     
	EventHandler h = new EventHandler(myImage);
	myImage.addMouseDownHandler(h);
	myImage.addMouseMoveHandler(h);
	myImage.addMouseUpHandler(h);

Now the event handler needs to do some things beyond just tracking where the mouse was clicked, where it is being dragged to, and how the universe should be changed as the dragging operation takes place. We also need to trap the event so we can handle dragging outside of our object (by capturing drag events), and we also have to prevent the event from percolating upwards, so we get the dragging events rather than the browser.

This means that our event dragging class looks something like:

private class EventHandler implements MouseDownHandler, MouseMoveHandler, MouseUpHandler
{
	private boolean fIsClicked;
	private Widget fMyDragObject

	EventHandler(Widget w)
	{
		fMyDragObject = w;
	}
		
	@Override
	public void onMouseUp(MouseUpEvent event)
	{
		// Do other release operations or appropriate stuff

		// Release the capture on the focus, and clear the flag
		// indicating we're dragging
		fIsClicked = false;
		Event.releaseCapture(fMyDragObject.getElement());
	}

	@Override
	public void onMouseMove(MouseMoveEvent event)
	{
		// If mouse is not down, ignore
		if (!fIsClicked) return;

		// Do something useful here as we drag
	}

	@Override
	public void onMouseDown(MouseDownEvent event)
	{
		// Note mouse is down.
		fIsClicked = true;

		// Capture mouse and prevent event from going up
		event.preventDefault();
		Event.setCapture(fMyDragObject.getElement());

		// Initialize other state we need as we drag/drop
	}

A FlexTable that handles mouse events.

Reverse engineering the GWT event handler code to add new events is simple. Here’s a Flex Table which also handles mouse events:

	/**
	 * Internal flex table declaration that syncs mouse down/move/up events
	 */
	private static class MouseFlexTable extends FlexTable implements HasAllMouseHandlers
	{
		@Override
		public HandlerRegistration addMouseDownHandler(MouseDownHandler handler)
		{
			return addDomHandler(handler, MouseDownEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseUpHandler(MouseUpHandler handler)
		{
			return addDomHandler(handler, MouseUpEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseOutHandler(MouseOutHandler handler)
		{
			return addDomHandler(handler, MouseOutEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseOverHandler(MouseOverHandler handler)
		{
			return addDomHandler(handler, MouseOverEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler)
		{
			return addDomHandler(handler, MouseMoveEvent.getType());
		}

		@Override
		public HandlerRegistration addMouseWheelHandler(MouseWheelHandler handler)
		{
			return addDomHandler(handler, MouseWheelEvent.getType());
		}
	}

Addendum:

If you want the location (the cell) in which the mouse event happened, you can extend MouseFlexTable with the additional methods:

		public static class HTMLCell
		{
			private final int row;
			private final int col;
			
			private HTMLCell(int r, int c)
			{
				row = r;
				col = c;
			}
			
			public int getRow()
			{
				return row;
			}
			
			public int getCol()
			{
				return col;
			}
		}
		
		public HTMLCell getHTMLCellForEvent(MouseEvent event) 
		{
			Element td = getEventTargetCell(Event.as(event.getNativeEvent()));
			if (td == null) {
				return null;
			}

			int row = TableRowElement.as(td.getParentElement()).getSectionRowIndex();
			int column = TableCellElement.as(td).getCellIndex();
			return new HTMLCell(row, column);
		}

GWT and tall images revisited.

As a follow-up to Things to Remember: Why cells with an inserted image are taller than the image in GWT, the answer is:

VerticalPanel panel;
...
Image image = new Image("images/mydot.png");
DOM.setStyleAttribute(image.getElement(), "display", "block");
panel.add(image);

For whatever reason, the image object has ‘inline’ formatting by default, and when GWT assembles the table cell, the cell’s height is being derived from the font height rather than from the image height. Setting the image to block seems to resolve this issue.

GWT Window.Location values.

Arcane knowledge: I wanted to know what values are returned for GWT’s Window.Location class.

For the URL “http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997&foo=555” the values are:

getHash:
getHost: 127.0.0.1:8888
getHostName: 127.0.0.1
getHref: http://127.0.0.1:8888/index.html?gwt.codesvr=127.0.0.1:9997&foo=555
getPath: /index.html
getPort: 8888
getProtocol: http:
getQueryString: ?gwt.codesvr=127.0.0.1:9997&foo=555

FYI…

Things to Remember: Why cells with an inserted image are taller than the image in GWT

So here’s a mystery.

Suppose you create in GWT a vertical panel or a flex table, and you add an image which is less than 15 pixels tall:

VerticalPanel panel;

...
panel.add(new Image("images/mydot.png"));

But for whatever reason, the cell displays as 15 pixels tall.

Apparently what happens is that the way the image object is inserted into the

object that makes up the vertical panel, an extra bit of text winds up being inserted alongside the image object.

And that blank has vertical height.

If you write the following, you can limit the vertical spacing, allowing for tighter heights:

Image image = new Image("images/mydot.png");
panel.add(image);
DOM.setStyleAttribute(image.getParent().getElement(),"fontSize","1px");

In testing this seems to tighten things up quite a bit.

I need to investigate this further. But apparently when DOM objects are being inserted during the construction of GWT objects, unwanted extra junk (in the form of blank text spaces) is being inserted at the same time.

Things To Remember: GWT Drag and Drop Edition

So I wrote the following code and wired it into the mouse up, down and move listeners so I can do drag and drop:

		@Override
		public void onMouseUp(MouseUpEvent event)
		{
			isDragging = false;
			// Do something to indicate we're done dragging
			
			DOM.releaseCapture(fClickLayer.getElement());
		}
		
		@Override
		public void onMouseMove(MouseMoveEvent event)
		{
			if (isDragging) {
				// Do something with the event to drag
			}
		}

		@Override
		public void onMouseDown(MouseDownEvent event)
		{
			DOM.setCapture(fClickLayer.getElement());
			// Note the initial state to start dragging
		}

And of course after the first three move events I stopped receiving move events, but instead the widget would be selected.

After banging my head against a brick wall for a couple of hours I realized I needed to prevent the browser from taking the default behavior on the received click and move events. And that’s done with the preventDefault() method:

		@Override
		public void onMouseUp(MouseUpEvent event)
		{
			isDragging = false;
			// Do something to indicate we're done dragging
			
			DOM.releaseCapture(fClickLayer.getElement());
		}
		
		@Override
		public void onMouseMove(MouseMoveEvent event)
		{
			if (isDragging) {
				event.preventDefault();		// ADD ME HERE
				// Do something with the event to drag
			}
		}

		@Override
		public void onMouseDown(MouseDownEvent event)
		{
			DOM.setCapture(fClickLayer.getElement());
			event.preventDefault();		// ADD ME HERE
			// Note the initial state to start dragging
		}

Duh.

As an aside, here’s a snippet of code that you can use to prevent something from being selected in HTML. You can also do this in CSS, I suppose. I encountered this snippet of code here.

	private native static void disableSelectInternal(Element e, boolean disable) 
	/*-{
    	if (disable) {
        	e.ondrag = function () { return false; };
        	e.onselectstart = function () { return false; };
        	e.style.MozUserSelect="none"
    	} else {
        	e.ondrag = null;
        	e.onselectstart = null;
        	e.style.MozUserSelect="text"
    	}
	}-*/;

I first tried hooking this up to the class receiving the mouse events, but to no avail.

Snippets of GWT

Strategy for validating text input in a TextBox during input:

One way to validate that the string being entered into a text box is properly formatted is to reject key changes as the user types, if the resulting string would result in an invalid string. The strategy I’m employing is to add a key press handler and schedule a deferred command: the deferred command then reverts the contents of the text box if the contents are illegal.

Thus:

public class TestEditorBox extends TextBox
{
    public TestEditorBox()
    {
        super();
        
        /*
         * Hang handler off key press; process and revert text of it doesn't
         * look right
         */
        addKeyPressHandler(new KeyPressHandler() {
            @Override
            public void onKeyPress(KeyPressEvent event)
            {
                final String oldValue = getText();
                Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
                    @Override
                    public void execute()
                    {
                        String curVal = getText();
                        if (!validate(curVal)) setText(oldValue);
                    }
                });
            }
        });
    }

    private boolean validate(String value)
    {
         // return true if the string is valid, false if not.
    }
...
}

GWT Weirdness Of The Day.

On the project I’m working on, I noticed that, for whatever reason, it was now taking up to 3 minutes to start a web site running under GWT. That is, from hitting the “Debug” button and opening the web site in the browser, to seeing the web site actually up and running, was taking just shy of three minutes, with most of that time with the Spinning Beach Ball Of Death™.

I finally figured out what was happening. The first clue came from hitting ‘pause’ on the Daemon Thread labeled “Code server for myProject from Safari DMP…”, and noticing a lot of time being spent walking around inside the source kit for resources.

And noticing a lot of that time was spent in a bunch of .svn folders.

I think what was going on is that, for some reason or another, a lot of crud had accumulated in a number of random hidden folders associated with subversion. I don’t know if this is normal or abnormal behavior for Subversion; I just know there was a bunch of crap floating around there–and GWT was spending several minutes walking around the directory structure.

Blowing away the entire source kit and checking it all out fresh from Subversion reduced the startup time to less than 5 seconds. And it greatly improved the performance of the browser in the debugger as well: rollovers which were taking a while to get detected now are fast and responsive.

I suspect all the crud in my source directory was creating a lot of crap for the debugger to deal with, creating a substantial slowdown that got cleared right up.