Thursday, November 29, 2007

A Glimmering Philosophy

In a previous post, Sneak Peak at Glimmer, I gave a basic introduction to the Glimmer JRuby DSL for SWT. In this post, I will go more into Glimmer's design philosophy and how Glimmer actually works. Glimmer was designed with these API goals in mind:
  • Concise and DRY (Don't Repeat Yourself)
  • Asks for minimum info needed to accomplish task
  • Convention over configuration
  • As predictable as possible for existing SWT developers
Here is a more advanced example of Glimmer than the one previously shown:
shell {
  text "Login"

  composite { 
  layout GridLayout.new(2, false)
    
    label { 
      text "Username:" 
    }
    text { 
      text account.user_name 
    }
    
    label { 
      text "Password:" 
    } 
    text(SWT::PASSWORD | SWT::BORDER) { 
      text account.password 
    }
        
    button { 
      text "Login" 
      on_widget_selected { |selection_event|
        account.login
      }
    }
  }
}
In Java, to nest SWT widgets under a parent widget, you write code like this: Label userNameLabel = new Label(parentComposite, SWT.NONE); Text userNameText = new Text(parentComposite, SWT.BORDER); As you can see, parentComposite was repeated for every nested widget. Glimmer is DRY when nesting widgets because you specify the parent widget and children widgets once, and the parent/child relationships are inferred automatically from the code structure:
composite {    
  label { text "Username:" } 
  text { text account.user_name } 
}
In Java, to set properties on a widget, you write code like this: groupBox.setText("Account Information"); groupBox.setFont(someFont); groupBox.setLayout(new GridLayout()); As you can see, groupBox was repeated multiple times, and so is the prefix "set" before every property. Glimmer is DRY and concise when it comes to setting properties because after you define a widget, you do not need to explicitly call it again to set its properties. Simply declare them one by one within the widget block:
group {
  text "Account Information"
  font someFont
  layout GridLayout.new
}
In Java, you must set a layout manager on every Composite widget, such as Shell, Composite, and Group. Otherwise, nested widgets are not automatically layed out on the screen (so you need to manually set absolute positioning.) Additionally, widgets always need a style even if 90% of the time you use SWT.NONE with Label and Composite for example. Glimmer demonstrates convention over configuration by addressing these API usability issues with intelligent defaults. For example, you do not have to specify a Composite widget's style and layout if you do not want to. Composite in Glimmer always defaults to SWT.NONE (SWT::NONE in JRuby) and GridLayout. There are intelligent defaults for every widget in fact; here is an example relying on a default Composite layout and style and default Text style:
composite { 
  text { text "Hello" } 
}
Still, you can customize them:
composite { 
  layout FillLayout.new
  text(SWT::PASSWORD) { text "SavedPassword" }
}
Finally, Glimmer is designed to be as predictable as possible for veteran SWT developers. This is achieved with a few simple rules:
  • Any widget available in SWT, including custom widgets written by developers, can be accessed from Glimmer by downcasing/underscoring the widget's name (e.g. Composite -> composite, LabledText -> labeled_text)
  • Style can be optionally specified following the name of the widget (e.g. text(SWT::PASSWORD). If it is not specified, the intelligent default is used instead (e.g. SWT.BORDER for Text)
  • Widgets are nested within other Composite widgets to describe parent/child relationships
  • Properties available on SWT widgets are specified by listing them followed by their values, each on a line or separated by semicolons within the widget's block (e.g. label {text "Username:"; font someFont})
  • SWT Listeners are defined by appending the "on" prefix to a listener method name (adjusted to follow the Ruby style convention) and following it with a block that receives the particular listener's event object (e.g. on_widget_selected {|selection_event| doSomething() })
That concluded demonstrating Glimmer API philosophies of being DRY, concise, favoring convention over configuration, and being as predictable as possible for existing SWT developers. Stay tuned for more details on how SWT listeners are supported in Glimmer.

No comments: