Advanced Concepts

Advanced or ambitious programmers may choose to use some of the additional features of ALE to make interface creation easier.

These powerful features include:


Defined symbols

ALE lets you define reusable variables within a section.

Suppose you want to define a gap between a set of buttons. You might use the following in your ALE file to accomplish this:


   section main
   {
      button1(p, p, 10, 10, LT)
      button2(p, p, button1.r + 10, 10, LT)
      button3(p, p, button2.r + 10, 10, LT)
   }
   

As an alternative, using the define feature, you could recode that section as


   section main
   {
      define
      {
         TOP = 10;
         GAP = 10;
      }

      button1(p, p, $GAP,             $TOP, LT)
      button2(p, p, button1.r + $GAP, $TOP, LT)
      button3(p, p, button2.r + $GAP, $TOP, LT)
   }
   

This feature allows you to reuse common values and expressions within your component definitions. If you decide to change the gap size to 20 pixels, modifying the GAP entry to read "GAP = 20" would automatically update the layout of your dialog.

You can also define one value in terms of another:


   section main
   {
      define
      {
         VALUE1 = 5;
         VALUE2 = $VALUE1 * 4;
         VALUE3 = $VALUE1 + $VALUE2;
      }
   }
   

There are a few caveats:

  • When you refer to a variable specified within the define section, you must preceed it with a "$"

  • Your defined variable names (TOP and GAP in this case) may not be the same as any of ALE's built-in attribute names (w, h, l, r, t, b, x, y).

  • There can be at most one define section and it must occur immediately after the "{" following the section definition.

  • This feature is unlike C's "#define feature" in that syntactical errors will be flagged even if the defined variable is never used.

Here is some sample code demonstrating this feature and the obligatory screen shot:


import java.awt.*;
import java.awt.event.*;
import com.sun.java.swing.*;
import jfd.ale.*;
import java.lang.reflect.Field;

public class define extends JFrame implements FieldToComponentMapper
{
   private JButton button1 = null;
   private JButton button2 = null;
   private JButton button3 = null;

   private static final String array[] =
   {
      "section main",
      "{",
      "   define",
      "   {",
      "      X_VALUE = w / 2;",
      "      Y_VALUE = h / 4;",
      "   }",
      "",
      "  button1(p, p, $X_VALUE,     $Y_VALUE, CC)",
      "  button2(p, p, $X_VALUE, 2 * $Y_VALUE, CC)",
      "  button3(p, p, $X_VALUE, 3 * $Y_VALUE, CC)",
      "}"
   };

   public define() throws Exception
   {
      addWindowListener(
         new WindowAdapter()
         {
            public void windowClosing(WindowEvent e) { System.exit(0); }
         }
      );

      button1 = new JButton("One");
      button2 = new JButton("Two");
      button3 = new JButton("Three");

      ALE.brew(array, "main", this, getContentPane());

      setSize(200, 200);
      show();
   }

   public Component mapToComponent(Field f)
      throws Exception
   {
      return((Component)f.get(this));
   }

   public static void main(String args[]) throws Exception
   {
      new define();
   }
}
   


External methods

ALE also gives you the ability to specify components' sizes or locations in terms of external method calls.

Suppose you have three buttons and you want to ensure that all the buttons are the same width. By observation, you can determine which button would be the widest and set the other buttons' widths to match, as in:


   section main
   {
      button1(p, p,         w / 2, h / 4)
      button2(p, button1.w, w / 2, 2 * h / 4)
      button3(p, button1.w, w / 2, 3 * h / 4)
   }
   

But this solution isn't optimal. If you change your Java code so that button2 becomes the widest button, all the buttons are still sized to button1's size. Also, with internationalization there may be no easy way to predict which of the buttons will be the largest.

ALE lets you use external methods in place of actual values in the ALE file. The above section (using this external method approach) would look like:


   section main
   {
      button1(p, getMaxButtonWidth(), w / 2, h / 4)
      button2(p, button1.w,           w / 2, 2 * h / 4)
      button3(p, button1.w,           w / 2, 3 * h / 4)
   }
   

ALE will use Java's reflection mechanism to call the method whose name you supply. There are a few stipulations, the method to be called must:

  • return a double
  • require no arguments
  • be public

The method must also reside within the class whose "this pointer" you supply as ALE.brew's third parameter. Here is a complete example using embedded (rather than external) layout information.


import jfd.ale.*;
import java.awt.*;
import java.awt.event.*;

public class ExternalMethodAWT extends Frame implements FieldToComponentMapper
{
   private Button button1 = null;
   private Button button2 = null;
   private Button button3 = null;

   private static final String array[] =
   {
      "section main",
      "{",
      "   button1(getMaxButtonWidth(), p, w / 2, h / 4,     CC)",
      "   button2(button1.w,           p, w / 2, h / 2,     CC)",
      "   button3(button1.w,           p, w / 2, 3 * h / 4, CC)",
      "}",
   };

   public ExternalMethodAWT() throws Exception
   {
      addWindowListener(
         new WindowAdapter()
         {
            public void windowClosing(WindowEvent e) { System.exit(0); }
         }
      );

      button1 = new Button("short");
      button2 = new Button("a bit longer");
      button3 = new Button("a fairly long button");

      ALE.brew(array, "main", this, this);

      setSize(640, 480);
      show();
   }

   public double getMaxButtonWidth()
   {
      Dimension d = button1.getPreferredSize();
      double max  = d.width;

      d = button2.getPreferredSize();
      if(d.width > max) max = d.width;

      d = button3.getPreferredSize();
      if(d.width > max) max = d.width;

      return(max);
   }

   public Component mapToComponent(java.lang.reflect.Field f)
      throws Exception
   {
      return((Component)f.get(this));
   }

   public static void main(String args[]) throws Exception
   {
      new ExternalMethodAWT();
   }
}
   

Here is a screenshot that shows what the resulting dialog would look like

As a final note, external methods can be used anywhere a value or component attribute would be used in your ALE file. As an example:


   section main
   {
      button1(p, getMaxButtonWidth(),            w / 2, h / 4)
      button2(p, 2 * getValue1(),                w / 2, 2 * h / 4)
      button3(p, getValue2() + getValue3()) / 3, w / 2, 3 * h / 4)
   }