How to build a Mars Application: first examples

Hi everyone!

So, most of the work I’ve done so far was about:

  • Refactoring code
  • Fixing bugs
  • Adding a few new widgets and event handling (for example: labels, buttons and press events, text inputs and keyboard events)

Now I will show you some examples and how to build a Mars application.

Installing Mars

Remember that Mars is working for Mac and Linux so far, and to install it on a Pharo image you need to evaluate this code:

Gofer it 
  url: 'http://www.smalltalkhub.com/mc/estebanlm/Mars/main'; 
  package: 'ConfigurationOfMars'; 
  load.

And then:

(ConfigurationOfMars project version: '0.2') load: 'All'.

The Mars Applications will use some icons that need to be initialized evaluating this line:

MarsToolIcons uniqueInstance initializeIcons.

If you are trying Mars on Linux, before going on please read how to set the environment for Mars on Linux.

Simple example: Copy text from a text area into a label

Yep, it’s an absolutely useless application. It just lets you write text into a text area (a MarsTextArea), then you press a button (a MarsButton) and the text is copied into a label (you guessed! a MarsLabel). But despite it’s totally useless for an end user, it might help you to understand how a Mars application can be built right now.

MarsApplication structure and template method

Take a look at this method:

MarsApplication>>open 
  window := self buildWindow. 
  self configure. 
  window title: self defaultWindowTitle. 
  window center. 
  window show. 
  self initializeApplication.

That is actually a template method, and the subclasses of MarsApplication are supposed to implement the methods buildWindow (to build the basic structure of a window with its widgets), configure (to configure how the widgets respond to the interaction with the user) and defaultWindowTitle (just a string of the first window title). If the application needs special extra initialization, then it can also redefine initializeApplication (by default it does nothing).

The orden of those messages is important: in initializeApplication you should set up the things in your application that need to be done after the widgets are shown. For example, if you try to change the text of a label before showing it, it won’t work.

So to build an application you can subclass MarsApplication and then implement what is needed. Let’s take a look at the methods implemented for the CopyTextFromTextToLabelExample application:

MarsCopyFromTextToLabelExample>>defaultWindowTitle 
   ^ 'Copy From Text to Label'
MarsCopyFromTextToLabelExample>>buildWindow 
  ^ MarsWindow new 
      extent: 800 @ 600; 
      layoutPolicy: MarsVerticalLayout new; 
      add: (MarsSplitter new beHorizontal; 
        add: (textBox := MarsTextArea new text: 'Some text that will be copied'); 
        add: (label := MarsLabel newWithLabel: 'Hello Mars!'); 
        add: (copyButton := (MarsButton newWithLabel: 'Copy!'))); 
      yourself

There you can see how a MarsWindow is built and composed. The MarsSplitter is a container, so the widgets are actually added to the splitter instead of directly to the window. I also save the widgets in instance variables of the application because I will use them for configuring the interaction of the user with the widgets:

MarsCopyFromTextToLabelExample>>configure 
  copyButton on: MarsButtonPressed do: [ label label: textBox text ]

The events of Cocoa and Gtk are translated into Pharo Announcements. So when the copyButton listens to a MarsButtonPressed announcement it sets the text of the textArea into the label. That’s it. Tadá!

Now, to test and open this application you only have to evaluate this in a workspace:

MarsCopyFromTextToLabelExample new open.

This example is already included in the Mars-Tools package on the repository. Here I show you the example in a little video so you can see how it looks in Mac and also Linux:

Less simple example: a System Browser built with Mars

The example of Mars should be a little native IDE of Pharo. This is a veeery humble first example showing a System Browser. It’s not complete, not only because it doesn’t let you edit anything, but also because you can only see instance methods of a class. I’m working on a segmented control widget that will help me complete that basic feature 😉

So, let’s see how MarsBrowser is implemented:

MarsBrowser>>buildWindow
  ^ MarsWindow new 
     extent: 800 @ 600; 
     layoutPolicy: MarsVerticalLayout new; 
     add: (MarsSplitter new beHorizontal; 
       add: (MarsSplitter new beVertical ; 
       add: (packagesTable := MarsList new); 
       add: (classesTree:= MarsTree new); 
       add: (protocolsList:= MarsList new); 
       add: (selectorsList:= MarsList new); 
       yourself); 
     add: (textArea := (MarsTextArea new)); 
     yourself); 
   yourself

This is a liiiitle more complex than the first example because there are two splitters: one for dividing the “package navigation” side from the “method source code” part. Then the “package navigation” side has it’s own splitter for the 4 lists.

Now, let’s see the configure method that it’s more complex than the previous one:

MarsBrowser>>configure 
  classesTree model 
    labels: [:c | c name]; 
    icons: [:c | self iconForClass: c]. 
  packagesTable model 
    list: self allPackages; 
    labels: [:p | p packageName]; 
    icons: [:p | self iconForPackage: p].
  protocolsList model 
    labels: [:p | p asString]. 

  packagesTable 
    on: MarsSelectionChanged 
    do: [:ann | 
      self selectPackage: ann selected. 
      self clearAll: {protocolsList . selectorsList } ]. 
   protocolsList 
    on: MarsSelectionChanged 
    do: [:ann | self selectProtocol: ann selected]. 
  classesTree 
    on: MarsSelectionChanged 
    do: [:ann | 
      self selectClass: ann selected. 
      self clearAll: { selectorsList }]. 
  selectorsList 
    on: MarsSelectionChanged 
    do: [:ann | self selectSelector: ann selected].

In this method first we set up somethings about the different lists: the models and the list themselves for the packages table, the classes tree, the protocols table and the selectors table.

Then we configure how they handle the user’s interaction using a MarsSelectionChanged announcement. This announcement holds the selected object. So for example, when a class is selected in the classes tree we can get the selected class and handle that like this:

MarsBrowser>>selectClass: aClass 
  window title: aClass name. 
  self selectedClass = aClass 
    ifFalse: [classesTree select: aClass]. 
  protocolsList model list: self selectedClassOrganization categoriesSorted.

Again, let me show you this better on a video 🙂

Bye!

5 thoughts on “How to build a Mars Application: first examples

    • Hi Carl!
      I was actually very close to use Cairo for implementing a PopOver in GTK, so I could draw my own window shape. It wasn’t a priority so in the end I didn’t do it, but I really want to try.

      I think that portable graphics implementation is an alternative and also a complement to Mars. An alternative in the sense that instead of rendering UIs with frameworks like Cocoa or GTK Mars could just use Cairo and have applications with the same look and feel in different platforms. Or a complement in the sense that Mars could render UIs in Cocoa, GTK… and Cairo if you wanted.
      But on the other hand, Mars goal is really let the user build applications that will look like the other applications that he can find running in his favorite OS. And it also takes advantage of widgets already built by the native UI frameworks instead of rebuilding them from scratch (like what it has to be done with Morphic-based UIs every time you want to add a new widget with new visual properties and new behaviour).

      Did you think of any application of Cairo for Mars?

      • Hi Carla!
        Thanks for replying. 🙂
        Oh there are so many uses for being able to draw graphics. Just think of all the applications that draw graphics, from games to art software to business charts. It doesn’t really need to be Cairo if we can figure out how to use Pharo’s built in graphics in a Mars window. What would be harder to do, Cairo or Pharo graphics?
        -Carl

  1. I did this:
    Gofer it
    url: ‘http://www.smalltalkhub.com/mc/estebanlm/Mars/main’;
    package: ‘ConfigurationOfMars’;
    load.

    and this:
    (ConfigurationOfMars project version: ‘0.2’) load.

    but then this:
    MarsToolIcons uniqueInstance initializeIcons.

    had ‘MarsToolIcons’ not defined.

    Working with the latest Pharo Image. Did I miss something?

    Thanks, Scott

Leave a comment