beans.jar, awt.jar and swing.jar. This will allow to just use for example BEANS or the AWT features alone (without having to download SWING even if you don't plan to use it). If Java and Nice are set up properly, then you can start using NiceSwing by simply specifying it in the classpath and make an import statement like import nice.ui.swing;.
...
JButton b = new JButton();
b.addActionListener(new ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
...
Of course the listener class can be any form of class java allows. A toplevel class as well as any form of inner class. In this example I used an anonymous inner class, as it's probably used most often.
WindowAdapter) or providing an empty implementation. It is also important to say that this way fits much better into Nice programming in general because it introduces a little functional style as opposed to the completely class centric approach in java. Also it is necessary to mention that the probaly most often used way in java (using (anonymous) inner classes) cannot be applied in Nice, as it lacks (anonymous) inner classes. Therefore, without NiceSwing it would be necessary to provide all listener classes as toplevel classes, which has a lot of disadvantages (visibility scope, namespace pollution and the problem of having references to the classes that should be manipulated in the listener code).
Here are some code examples:
...
JButton b = new JButton();
b.addActionListener(ActionEvent e => println("Hello Nice World"));
...
In this example we say that whenever an ActionEvent is fired on b we call the method println. The (Nice) method addActionListener takes one listener function as an argument (besides the first 'implicit' argument). In such cases - where a listener only defines one method - the listener function passed as parameter is non-optional, as it makes no sense to add a null listener. If a listener class defines more than one method, those methods are optional parameters that have null as default value. This is done to provide the above mentioned ability, to specify only those listener methods that are really needed. Here is an example for this usage:
...
JPanel p = new JPanel();
p.addMouseListener
(mouseEntered: MouseEvent e => println("Hello Nice World"),
mouseExited: MouseEvent e => println("Goodbye Nice World")
);
...
Here we only use the two methods mouseEntered and mouseExited from the java.awt.event.MouseListener interface. We can simply leave out the ones we don't want, as they have null as a default value.
java.awt.event, javax.swing.event or java.beans. All operations concerning listeners must be done on 'class level'. When you add a listener, you must add an instance of a class implementing a listener interface. When you want to remove a listener, you must pass the listener object to remove to the removeFooListener method.
NiceSwing handles this differently. Listeners can be manipulated on 'method level', meaning that you can add listeners as methods and also remove them as methods. The only operation that remains (more or less) the same is accessing a listener. When you call getFooListeners on a listener handling class you will get back an optional array of all listener class instances added to this class. You get back the classes into which the methods were wrapped when adding them. It wouldn't make any sense to stay on 'method level' here (meaning you get back an array of listener methods that were added), as there are listener interfaces that define more than one method (as mentioned above, e.g. java.awt.event.WindowListener). It would be possible to return a tuple consisting of arrays filled with listener methods, but I guess this complicates the situation too much. Nonetheless because of syntax issues when using tuples.
Still, NiceSwing has one advantage over pure Swing when accessing listeners. When you ask a class for its ActionListeners using getActionListeners method, you get back an optional array of non-optional ActionListeners. This is different in java where you always get back an optional array of optional ActionListeners as java's type system is too weak to express non-nullness.
Also it has to be said that it's only possible since JDK1.4 to call e.g. getActionListeners and getting back an array of ActionListeners. Before JDK1.4 one could only call EventListener[] getListeners(Class c) where c specified the type of the listeners to get out, and the return type was java.util.EventListener[]. Thus it was necessary to cast the value returned from the getListener method to the type supplied in c which should absolutely not be necessary. NiceSwing is compatible to JDK1.3 and nevertheless allows to access registered listeners without having to cast them!
java.awt.Component, java.awt.Container and javax.swing.JComponent. Retypers for methods of these 3 classes are completely already, as they build the basic foundation for all AWT and Swing Widgets. To exactly know if a method is already retyped, it is advised to look into CVS! If you encounter a method you need to retype in your project and it's not yet in NiceSwing , please take the minute and contribute your implementation to CVS. I talked to DanielBonniot and we agreed to wait and see if it is sufficient to let the retyping section grow in this way, or if it will be necessary to complete this task once and for all.
...
HelloPanel panel = new HelloPanel("come inside");
// --------------------------------------------------------------------------------------
// HERE we add two MouseListener methods as listeners to the corresponding events
// NOTE that we just add two of five listener methods
panel.addMouseListener
(mouseEntered: (java.awt.event.MouseEvent e) => { panel.setGreeting("Hello Nice World"); },
mouseExited: (java.awt.event.MouseEvent e) => { panel.setGreeting(" Bye Nice World"); }
);
// HERE we add one WindowListener methods as listener to the corresponding event
// NOTE that we just add one of seven listener methods
JFrame frame = new JFrame("Hello new swinglib API")
frame.addWindowListener
(windowClosing: java.awt.event.WindowEvent e => System.exit(0)
);
// --------------------------------------------------------------------------------------
This example shows how to add event listeners using NiceSwing . Besides the way described above, the frame.addWindowListener(...) code shows that this of course can also be done with anonymous functions.
Note that this example does not provide support to remove the added listeners afterwards. To achieve this, references to the methods added as listeners must be kept. This usage is shown in detail below, where the removal of event listeners is explained.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import nice.ui.common;
// -------------------------------------
// HERE we store the references to the
// listener methods. This is needed for
// later removal.
var ActionEvent -> void blueColor;
var ActionEvent -> void offBlueColor;
// -------------------------------------
// -------------------------------------------------------------
// HERE we define the two listener methods that should be called
// when the events of interest are fired
// changes background color of p to c
void changeColor(JPanel p, Color c)
= p.setBackground(c);
// removes action from button
// HERE we actually remove the listener method action from
// button's listener list
void offButton(JButton button, ActionEvent -> void action)
= button.removeActionListener(actionPerformed: action);
// -------------------------------------------------------------
JPanel makePanel()
{
// create the panel
JPanel panel = new JPanel();
// switches background to blue
// HERE we store the reference to blueButton's action
blueColor = (ActionEvent e) => changeColor(panel,Color.blue);
JButton blueButton = new JButton("BLUE");
blueButton.addActionListener
(actionPerformed: blueColor
);
// switches off grayButtons action
// HERE we store the reference to offBlueButton's action
offBlueColor = (ActionEvent e) => offButton(blueButton, blueColor);
JButton offBlueButton = new JButton("Switch off BLUE");
offBlueButton.addActionListener
(actionPerformed: offBlueColor
);
// add the 2 buttons
panel.add(blueButton);
panel.add(offBlueButton);
return panel;
}
void main(String[] args)
{
...
JPanel p = makePanel();
...
}
This example illustrates how to remove event listener methods from classes that can handle them. It does this by providing the ability to 'switch off' Button=s. A =Button is switched off by removing it's previously added listener method from it's listener list, thus no more action is performed.
// The same signature is used for other classes that need this method
// but do not inherit from java.awt.Component.
// NOTE that the return type written in Nice would be:
// ?java.util.EventListener[?]
java.util.EventListener[] java.awt.Component.getListeners(Class c);
// typical usage in JAVA
...
JButton b = ...;
ActionListener[] l = (ActionListener[])b.getListeners(ActionListener.class);
if(l != null) { ... }
...
but instead can call e.g.
...
JButton b = ...;
ActionListener[?] l = b.getNActionListeners(); // NOTE the 'N'
if(l != null) { ... }
...
Note the N used to distinguish the Nice method from the Java pendant. This is necessary to prevent ambiguities, as the signature of the method is completely the same in Java and Nice. In the Nice version we don't need to cast and moreover, we can also be sure that the listeners inside the array are non-optional. This is stated in javadoc but cannot be expressed by Java's type system.
JDK1.4 also introduces exact listener accessor methods which means you can also call in Java 1.4
... JButton b = ..; ActionListener[] l = b.getActionListeners(); ...The good thing is that users of NiceSwing can use the same functionality also in JDK1.3. Another issue with Java is that it is not possible for every class that allows to add and remove listeners, to also access those listeners. I don't know why this is, but DanielBonniot and I came to the conclusion that there is no reason to prevent this actually. So NiceSwing provides the ability to access every listener that can be added or removed. In general, if a class allows one of the operations add remove access it MUST provide the other two as well.
java.util.EventListener. It is possible to call
...
anyListenerHandler.init
( ...
...
);
...
An important thing to mention is that init always accepts only those listeners that can really be handled from the class that it is called on. It is therefore e.g. not possible to add a WindowListener to a JButton, since a JButton does not know how to handle =WindowEvent=s .This is because init itself is overridden for every listener handler. It cannot be a multimethod since not all possible implementations have the same number and type of parameters.
addFooListener methods but add all listeners with one statement. Because the init methods return a value they can be called immediately after the new call:
JButton b = new JButton().init (actionPerformed: ActionEvent e => action1(e) mouseEntered: MouseEvent e => action2(e) );
...
HelloPanel panel = new HelloPanel("come inside");
panel.addMouseListener
(mouseEntered: (java.awt.event.MouseEvent e) => { panel.setGreeting("Hello Nice World"); }, // HERE we need panel as a parameter
mouseExited: (java.awt.event.MouseEvent e) => { panel.setGreeting(" Bye Nice World"); } // HERE we need panel as a parameter
);
...
project | + src | | + projectname | + classes | + lib | | + niceswing.jarin folder project :
nicec --classpath ./lib/niceswing.jar --sourcepath ./src --destination ./classes projectname
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class HelloPanel extends JPanel
{
private String greeting = "come inside";
HelloPanel()
{
this.addMouseListener(new MouseAdapter() {
public void mouseEntered(MouseEvent e) {
HelloPanel.this.setGreeting("Hello Java World");
}
public void mouseExited(MouseEvent e) {
HelloPanel.this.setGreeting(" Bye Java World");
}
});
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.blue);
g.drawString(this.greeting,5,40);
}
void setGreeting(String s)
{
this.greeting = s;
this.repaint();
}
}
class HelloFrame extends JFrame
{
public HelloFrame()
{
super("HelloSwingWorld");
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.getContentPane().add(new HelloPanel());
this.setSize(100,100);
this.show();
}
}
public class HelloSwingWorld
{
public static void main(String[] args)
{
new HelloFrame().show();
}
}
I know that this functionality can be coded within one class and a little bigger main method also. Still this is how Sun press teaches you how to write well structured Swing code! Let's look at the much shorter and definitely equally well structured Nice pendant below.
package examples.helloworld;
import nice.ui.common;
import javax.swing.*;
class HelloPanel extends JPanel
{
private String greeting;
void setGreeting(String);
}
paintComponent(this@HelloPanel,g)
{
super;
g.setColor(java.awt.Color.blue);
g.drawString(this.greeting,5,40);
}
setGreeting(this@HelloPanel,s)
{
this.greeting = s;
this.repaint();
}
void main(String[] args)
{
HelloPanel panel = new HelloPanel(greeting:"come inside");
panel.init
(mouseEntered: (java.awt.event.MouseEvent e) => { panel.setGreeting("Hello Nice World"); },
mouseExited: (java.awt.event.MouseEvent e) => { panel.setGreeting(" Bye Nice World"); }
);
JFrame frame = new JFrame("Hello new swinglib API").init
(windowClosing: java.awt.event.WindowEvent e => System.exit(0)
);
frame.getContentPane().add(panel);
frame.setSize(100,100);
frame.show();
}
...
ActionEvent -> void action = ActionEvent e => System.exit(0);
JButton b = new JButton().init
(actionPerformed: action // removable listener
mouseEntered: MouseEvent e => println("hello") // not removable listener
);
...
class NiceControlListener implements org.eclipse.swt.events.ControlListener
{
private ControlEvent ?-> void controlMoved = null;
private ControlEvent ?-> void controlResized = null;
public ControlEvent ?-> void getControlMoved() = this.controlMoved;
public ControlEvent ?-> void getControlMoved() = this.controlMoved;
public void setControlMoved(ControlEvent ?-> void controlMoved) { this.controlMoved = controlMoved; }
public void setControlResized(ControlEvent ?-> void controlResized) { this.controlResized = controlResized; }
controlMoved(e)
{
ControlEvent ?-> void controlMoved = this.controlMoved;
if(controlMoved != null) controlMoved(e);
}
controlResized(e)
{
ControlEvent ?-> void controlResized = this.controlResized;
if(controlResized != null) controlResized(e);
}
}
In the body of the overridden methods you copy the optional instance variable into a local variable. Then ask it if it is null and if this is not the case, you call the method stored inside the (now) local variable. So that whenever the framework calls the method(s) defined in the listener class this will lead to execution of the methods you added using the automatically generated constructor for the listener class. This is basically bridging the gap between the class based approach in the GUI toolkits to integrate, and the functional approach in the Nice programming language. You define wrapper classes for your listener method pointers.
2) Then you define an abstract interface FooListenerHandler extending the abstract interface nice.ui.common.NiceListenerHandler for every Listener that is defined in the toolkit. Now you have as much abstract interfaces as listeners interfaces (or equally your listener wrapper classes).
abstract interface ControlListenerHandler extends NiceListenerHandler; abstract interface FocusListenerHandler extends NiceListenerHandler; ...You can now use those new abstract interfaces to mark all classes in the toolkit that can handle the listeners. 3) So the next step is to walk through all widget classes (more general, all classes that can handle a listener using add/remove/get methods) in the toolkit and say that they implement all the abstract interfaces (the ListenerHandlers?) that you have defined in the above step 3).
class org.eclipse.swt.widgets.Control implements ControlListenerHandler; class org.eclipse.swt.widgets.Control implements FocusListenerHandler; ...Now for each class you have as much
class X implements FooListenerHandler? declarations as class X can handle listeners. For example you have 9 such declarations in class org.eclipse.swt.widgets.Control since this class can handle 9 different listeners.
4) Now you define methods and functions for all newly created abstract interfaces: 2 multimethods addNiceFooListener and getNiceFooListener and 2 functions addFooListener and removeFooListener. The multimethods are used to guarantee at compile time that you didn't forget an implementation of add or remove, (as long as you have marked EVERY class that can handle a listener as a ListenerHandler). Look through the code below to see how it's done.
// you have to make this multimethod exhaustive, so if you haven't forgotten to define
// a FooListenerHandler abstract interface and marked all classes that can handle this FooListener as // FooListenerHandlers, the compiler will force you to provide an implementation for every
// ListenerHandler in the toolkit to integrate
public <ControlListenerHandler H> void addNiceControlListener(H,NActionListener);
// now you can write a function that takes all listener methods as parameters, wraps
// them up in the according listener class and adds them to the specified ListenerHandler
public <ControlListenerHandler H> void addControlListener
(H this,
ControlEvent ?-> void controlMoved = null,
ControlEvent ?-> void controlResized = null
)
{
this.addNiceControlListener
(new NiceControlListener
(controlMoved: controlMoved,
controlResized: controlResized
));
}
// again this is a multimethod that wants to be exhaustive for all ListenerHandlers
public <ControlListenerHandler H> NiceControlListener[?] getNiceControlListeners(H);
// we can use the getNiceControlListeners method in this function to be sure that we can
// search the listener method(s) to remove, in every ControlListenerHandler H
public <ControlListenerHandler H> void removeControlListener
(H this,
ControlEvent ?-> void controlMoved,
ControlEvent ?-> void controlResized
);
{
NiceControlListener[?] listeners = this.getNiceControlListeners();
if(listeners == null) return;
listeners.foreach
(NiceControlListener l =>
{
if(controlMoved != null && l.getControlMoved() == controlMoved) l.setControlMoved(null);
if(controlResized != null && l.getControlResized() == controlResized) l.setControlResized(null);
}
);
}
If you are finished doing this for all Listeners and ListenerHandlers in the desired toolkit, then you have succesfully integrated this toolkit into nice.ui. You are now able to add , get and remove all listeners defined in the toolkit to integrate. If you also want to provide WidgetInitializers? like NiceSwing does, you have to follow the last step.
5) Up to every leaf class of the toolkit (where leaf in this context means that subclasses of the class don't handle any more additional listeners), you define an init function that takes ALL listener methods of ALL listeners the class can handle. You do this for every class up the way to the leaf so that you can call superclass implementations to make life easier. So if you have for example a class that is able to handle 3 listeners, all of which have 3 listener methods, then you will have to provide an init function taking the ListenerHandler? to be called on as the first argument, and the 9 listener methods as optional parameters that default to null. I will use an example out of NiceSwing here (as I actually haven't started coding for the SWT extension of nice.ui yet). I will use the init method for java.awt.Component as it seems to be the equivalent to org.eclipse.swt.widgets.Control .
<Component T> T init
(T this,
// optional anonymous functions from java.awt.Component
?String propertyName = null,
ComponentEvent ?-> void componentHidden = null,
ComponentEvent ?-> void componentMoved = null,
ComponentEvent ?-> void componentResized = null,
ComponentEvent ?-> void componentShown = null,
FocusEvent ?-> void focusGained = null,
FocusEvent ?-> void focusLost = null,
HierarchyEvent ?-> void hierarchyAncestorMoved = null,
HierarchyEvent ?-> void hierarchyAncestorResized = null,
HierarchyEvent ?-> void hierarchyChanged = null,
InputMethodEvent ?-> void caretPositionChanged = null,
InputMethodEvent ?-> void inputMethodTextChanged = null,
KeyEvent ?-> void keyPressed = null,
KeyEvent ?-> void keyReleased = null,
KeyEvent ?-> void keyTyped = null,
MouseEvent ?-> void mouseClicked = null,
MouseEvent ?-> void mouseEntered = null,
MouseEvent ?-> void mouseExited = null,
MouseEvent ?-> void mousePressed = null,
MouseEvent ?-> void mouseReleased = null,
MouseEvent ?-> void mouseDragged = null,
MouseEvent ?-> void mouseMoved = null,
PropertyChangeEvent ?-> void propertyChange = null
//JDK1.4 MouseWheelEvent ?-> void mouseWheelMoved = null
)
{
this.addComponentListener
(componentHidden: componentHidden,
componentMoved: componentMoved,
componentResized: componentResized,
componentShown: componentShown
);
this.addFocusListener
(focusGained: focusGained,
focusLost: focusLost
);
this.addHierarchyBoundsListener
(hierarchyAncestorMoved: hierarchyAncestorMoved,
hierarchyAncestorResized: hierarchyAncestorResized
);
this.addInputMethodListener
(caretPositionChanged: caretPositionChanged,
inputMethodTextChanged: inputMethodTextChanged
);
this.addKeyListener
(keyPressed: keyPressed,
keyReleased: keyReleased,
keyTyped: keyTyped
);
this.addMouseListener
(mouseClicked: mouseClicked,
mouseEntered: mouseEntered,
mouseExited: mouseExited,
mousePressed: mousePressed,
mouseReleased: mouseReleased
);
this.addMouseMotionListener
(mouseDragged: mouseDragged,
mouseMoved: mouseMoved
);
if(propertyName != null && propertyChange != null)
this.addPropertyChangeListener
(propertyName: propertyName,
propertyChange: propertyChange
);
if(propertyName == null && propertyChange != null)
this.addPropertyChangeListener
(propertyChange: propertyChange
);
//JDK1.4 this.addMouseWheelListener(mouseWheelMoved: mouseWheelMoved);
return this;
}
Having finished all those init functions you can now use the newly integrated toolkit in the same way you would use NiceSwing.
| Topic NiceSwing . { Edit | Attach | Ref-By | Printable | Diffs | r1.3 | > | r1.2 | > | r1.1 | More } |
|
Revision r1.3 - 31 May 2003 - 01:32 GMT - TWikiGuest Parents: WebHome |
Copyright © 1999-2003 by the contributing authors.
All material on this collaboration platform is the property of the contributing authors. Ideas, requests, problems regarding TWiki? Send feedback. |