TWiki . Doc . NiceSwing

On this page you will find the documentation of NiceSwing. I structured it a little like a FAQ not only because it made it somewhat easier for me to structure, but also because I think this will maybe also be able to answer some precise questions that occur when using NiceSwing, without having to read through the whole documentation.

You can download the library here: NiceSwingFiles

Introduction

In this section you will get info on what NiceSwing is and what it's not. What it can do for you and what you need in order to use it. There are no concrete examples in this section. Still you will know enough to decide if NiceSwing can do something for you, after reading this.

It is important to mention here that the reader should know about programming event handling in pure Java Swing and should be familiar with the Nice programming language. There are some examples on Swing code (to point out the differences to NiceSwing) but there are no explanations on Nice's advanced features. If you are not yet familiar with Nice itself I suggest you read the NiceTutorial before and come back later :-)

What is it

NiceSwing is a project to provide a Nice library on top of Swing, in order to use Nice's advanced features to program Swing applications. It makes it is easy to handle adding, removing and accessing of all event listeners defined in

to and from all appropriate classes defined in

and of course your own (Nice) classes derived from existing java classes. NiceSwing (as the name proposes) is mainly targeted at programming Swing hence GUI applications. The goal is to integrate programming Swing with the way of programming in Nice. This means especially getting rid of having to define listener classes all the time when writing Swing GUIs, but instead registering 'methods' as listeners that are invoked when the according event is fired. This leads to much cleaner and shorter code and also has several other advantages that will be pointed out later in this document.

What is it not

It is no new Graphical Environment in itself, it is completely written on top of Swing. It does not provide any new GUI Widgets, nor does it add any classes to Swing. It does not provide new functionality, it just lets you use the same functionality in another (nicer) way.

What it can do

Using it you can integrate programming GUI applications with the way you would normally program using the Nice programming language. It makes it easy to handle event listeners as toplevel methods, thus not always having to provide a class implementing the corresponding event interface. This can help you save writing a lot of code (class declarations, empty default implementations).

What it cannot do

It currently cannot handle listeners that are specified in java.beans.beancontext. This will most likely change some time. For now it's mainly targeted at programming GUI applications using Swing or AWT.

What is needed to use it

First of all you need a JDK from Sun, which is necessary for Nice itself anyway. Of course you need the Nice compiler (at least version 0.7.4) also. Then you should get the NiceSwingFiles containing the library. Currently there is only one jar file containing the whole library. Later, when the Nice compiler will allow this, there will be different jar files for each package: 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;.

Who should use it

Everyone developing GUI (either Swing or AWT) applications using the Nice programming should really consider using it. It's main advantage is that it lets you program Swing applications, the way you would expect to do it in Nice. The philosophy behind it (i.e. the Listener Concept) remains the same, only it is done in another way. You don't add classes as listeners anymore, but methods, which is more natural (especially in a language like Nice).

Comparison with pure Swing

Since Nice is a new programming language there are differences in programming Nice and Java. In this section I try to point out the main differences when coding event handling in those two languages and why it is preferrable to use NiceSwing when programming in the Nice programming language. I will give examples from Java and ones from Nice and I'll show where they have something in common and where they differ from each other.

Event Listeners in Java

I guess everyone reading this tutorial is familiar with the way events are handled in java. Nevertheless I show a small example here, to be able to clearly point out the differences to NiceSwing. What you normally do in java is that you define a class implementing the desired event listener interface, and then register this class as the according event listener to a class that knows how to handle this listener. Here is a small code example:

...
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.

Event Listeners in Nice

NiceSwing is a layer on top of Swing. What it does is that it wraps up the methods passed as listeners into the according event listener class and then add it to the event handling class in the normal java way. This process is hidden in the library, so the user is able to register a listener as a method. This has several advantages. First of all it's far less syntactic overhead which leads to shorter code. Secondly, it provides the ability to just add the listener methods that are really needed, without having to use Wrapper classes as defined in java (e.g. 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.

What are the differences between NiceSwing and pure Swing

The main difference is that in java, as it is a totally class centric language, every listener must be an instance of a class implementing a listener interface as defined in 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 ActionListner=s using =getActionListeners method, you get back an optional array of non-optional =ActionListener=s. This is different in java where you always get back an optional array of optional =ActionListener=s 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 ActionListener=s. 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!

What do NiceSwing and pure Swing have in common

As NiceSwing is a layer on top of Swing, they both share the same concept of event handling, namely the usage of listener classes (only that those classes are (normally) not seen by the user in NiceSwing). Still it is also possible in NiceSwing to add listeners on 'class level' by defining listener classes on toplevel and then adding them using the 'normal' java methods, but this is definitely not the way it should be done. This is possible because NiceSwing uses this layer itself to add the listener classes.

Why is NiceSwing preferrable to pure Swing

It is preferrable to pure Swing because of a few reasons. The first could be that Nice does not support any form of inner classes as known from java. So without NiceSwing, it would therefore only be possible to delare all listener classes as toplevel classes which will most likely lead to a bad design, as there are a few problems associated with a situation like this. One is that this really pollutes the (package) namespace as there are a lot of classes on top level. Another would be that listener classes are accessible in contexts where they should not be accessible. Furthermore a situation like this would lead to the problem of having references to classes that should be manipulated within the event code, in place. This could lead to listener classes having a lot of instance variables in which references to the above mentioned classes are stored. This is definitely neither necessary nor desirable.

Another reason to prefer NiceSwing is that it is integrated well with the way of programming in Nice. A programmer familiar with Nice will soon feel familiar with this other way of event handling, as it makes use of Nice's advanced features that (in my opinion) feel much more natural in situations like event handling.

One big advantage of course is, that in most situations usage of NiceSwing will lead to shorter code as there is far less syntactical overhead involved.

Performance issues

This is still an open question. As there is no real world application written with NiceSwing yet we don't know if there are any performance drawbacks. But we expect there will be none!

Functionality

This section describes in detail how to use the different elements of NiceSwing in order to be able to achieve the same functionality like pure Swing event handling. In this section there are a lot of small examples to show you how the code looks like. After reading this you should know enough about NiceSwing to be able to use it in applications.

Elements

NiceSwing consists of the following elements:

Together they provide the functionality to do equally powerful (yet differently implemented) event handling as pure Swing mechanisms, with the advantage to be (syntactically, conceptually) fully integrated into the Nice programming language.

Precise Types

The Nice feature of being able to give more precise types to existing java methods is widespreadly used in NiceSwing. The ultimate goal would be to have all methods in java.awt and javax.swing (and a few supporting packages) retyped. This is not realized yet, although already a lot of methods of those 2 main packages are already retyped.

It is important here to say that I retyped the methods basically along javadoc which means I didn't do it for classes that I think are used very often, but mostly just in alphabetical order (starting with the first class in the according javadoc package , and down the line ...). I did this because it would be tedious to always look up if I already retyped a certain method because I found it important. There are a few exceptions from this concept for classes I consider really important , like java.awt.Component , java.awt.Container and javax.swing.JComponent . Retypers for methods of these 3 classes are complete 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.

Adding Event Listeners

It is very easy to add event listeners to classes that are able to handle those listeners. All you need to do is to define a method with return type void that contains the code that should be executed when the according event is fired, and add this method as a listener. This method can take arbitrary parameter(s) but it has to be remembered that it is only useful to give this method parameters that are present at call site (known in the context). To make things more concrete, here is an example:

... 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.

Removing Event Listeners

Removing event listeners is done in very similar way. You just specify the listener method to remove as a parameter to the removeFooListener method. This automatically implies the above mentioned fact, that a reference to the added listener method must be in place, in order to remove this method from the object's list of listeners. Note that this is similar in Java, where you have to keep a reference to the added listener object if you want to remove it later, and pass this reference to the removeFooListener method. Here is an example:

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' Buttons. A Button is switched off by removing it's previously added listener method from it's listener list, thus no more action is performed.

Accessing Event Listeners

Accessing event listeners is done in the same way as in Java, but it still has several advantages. First of all NiceSwing introduces for JDK1.3 the ability to get 'exact' listeners out of the class that registered them. This means that you don't need to call

// 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 .

Widget Initialization

The below described init method can be applied to any java class that is able to handle any listener that implements 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 WindowEvents? .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.

What it can do

The init methods provide a way to initialize a GUI widget with all the possible listener methods it can handle. It is thus not necessary to subsequently call different 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) );

What it cannot do

The init methods provide no way to initialize a widget with additional properties other than the listeners they can handle.

The above way of calling init directly after new is not possible if the newly created object is used as a parameter to one of the listener methods. This is because the object to be passed as parameter is not fully created at that time. This leads to the need to split this operation up into one constructor call and the following call to init . Here is an example:

... 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 );
...

Using NiceSwing

Getting the library

To get the NiceSwing library, simply follow this NiceSwingFiles link, and you will come to a page where you can download the library and the build file. Currently it is attached to a WikiPage??, this will maybe change later.

Compiling programs

In general I would really advise you to use Ant to build (any of) your Nice programs. However, programs using NiceSwing can also be compiled using nicec alone.

A typical usage of nicec would be:

project | + src | | + projectname | + classes | + lib | | + niceswing.jar

in folder project : nicec --classpath ./lib/niceswing.jar --sourcepath ./src --destination ./classes projectname

A short Java example

This example shows a little more than the classic helloworld example. It shows a frame which includes a JPanel that is sensible for MouseEvents? . When the mouse is dragged inside the frame a greeting is displayed inside the frame. When the mouse is dragged outside of the frame a goodbye message is displayed. Here is the Java version of this code:

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.

A short NiceSwing example

Here is the same helloworld example again but this time written using NiceSwing. It shows how to use listener decorators and widget initializers. Note that it's much shorter than it's Java pendant and definitely equally well strucured.

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?("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(); }

Influence on Code Design

Of course using a new API has some influences on how to design and structure new code that you write. So it is with NiceSwing . Generally it is one of the goals to integrate event handling coding inot the way of programming in the Nice programming language. So basically if you feel comfortable with Nice in general, you should also feel comfortable with the way NiceSwing handles event handling. Still it requires some general changes compared to normal Swing programming. This is the reason why this section will mostly show how to organize code using NiceSwing and compare this to the way this would be done in Java using Swing.

Naming Conventions

There are no special conventions to name your methods and classes other than what one knows from Java and what is called good style.

How should a NiceSwing GUI application be organized

Below you find a few tips on how to organize your (event) code when programming with NiceSwing. This should give you an insight into what you might have to change when used to programming pure Swing.

Packaging

Everything stays the same concerning packaging. You can package your code however you want, in general it may be a good idea to put all GUI code into a package (with possible subpackages). I should think it is not necessary in general to put event code into own packages, nevertheless this may change from situation to situation so there is no guideline for that.

An important thing to mention is that nicec currently implies some restrictions if abstract interfaces are involved into your Nice code. I imported a detailed explanation from DanielBonniot? from an email discussion and added it to the AbstractInterfacesPackagingProblem page. Basically, what you cannot do is to let a non package-local class implement a non package local abstract interface.

Event Code Localization

Depending on the size of your project it is maybe a good idea to put event code into separate files where you put all listener method definitions. If your project is not too large you may stick to writing anonymous functions as your listener code. It is up to you if you like to write extra 'header' files only containing the declaration of the methods. I must say I generally like the idea to write 'header' files, as they make it easy to provide a few files that give a good overview of what the code is about, by collecting all declarations, thus showing the whole 'API' of your code. Maybe it is also a good idea to just collect all public declarations in the 'header' files, as this maybe reflects more the idea of what an API is. Maybe this is even necessary as I think DanielBonniot? is thinking about restricting private code visibility to the file it is declared in. I am not sure about that at the moment, I will update this section if necessary later.

Differences to coding pure Swing

There are some differences compared to pure Swing when coding with NiceSwing. They concern the structure of the program (packaging) and the different concepts in use in Nice and Java which lead to a rather different approach in Nice.

Methods vs. Classes

As Nice supports no InnerClasses? the only way to code event handling with Swing would be to make toplevel classes implementing the desired event listener interface. This is definitely not good practice for a few reasons explained above in this tutorial. Moreover it doesn't feel natural in a programming language like Nice to achieve this functionality by providing classes. It is much easier to just define methods that perform the desired operations and register this methods as listeners, so that whenever the according event is fired, this method is executed. This idea is easy to integrate in Nice, but impossible in Java.

Files vs. Classes

In Java it is common practice to put one class into one file and name the file after the (public) class that's in it. In Nice it is not necessary (and also not practice) to put only one class into one file. What should then be done with the toplevel methods ? So it is normal in Nice to use a file as a logic grouping of classes and methods that together perform a job. If you want you can see the file as a logic unit between classes and packages. This is encouraged also when you look at the proposal from DanielBonniot? that the private visibility modifier should restrict visibility to the file it was declared in.

Anonymous Functions vs. Inner Classes

Since Nice provides no InnerClasses? and thus no anonymous inner classes it is not possible to write listeners using one of this (in Java very common) techniques. Luckily Nice provides anonymous functions that fit exactly into the concept of NiceSwing. Instead of defining a listener as an anonymous class in Java you can define a listener as an anonymous function in Nice. The only difference when doing this is that you are not able to remove this listener afterwards if you don't store a reference to this anonymous function in an variable. Example code would be like:

... 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 );
...

Internal Structure

This section provides detailed information on how NiceSwing internally works. These sections mostly originate from the Developer Documentation. Everyone who wants to know how NiceSwing works, who just wants to see some (more) Nice code or who believes looking through this code will help him understand how to code using NiceSwing better, is invited to do so!

How is it organized

You will find more on how everything is organized when looking into the Developer Documentation under Structure

How does it work

You will find more on how everything is organized when looking into the Developer Documentation under public interfacewhich is currently under development so there may be some inconsistencies with code in CVS. I will fix this soon!

How can it be extended

First of all we have to define what can be extended. Well, we could extend it in a way that let's us use other GUI frameworks in the same manner we can now use Swing or AWT. I will discuss this issue using the example of integrating SWT support as I will do this when I start working on the EclipsePlugin? (SWT is the GUI toolkit used in the Eclipse Framework). It has to be said that this section is not only a description of how NiceSwing can be extended, but also of how it is designed itself. I followed exactly those steps (well it took me (and DanielBonniot?) some time to find them out before) to build NiceSwing

Basically what you have to do when integrating a new GUI toolkit is this:

1) For every listener interface in the toolkit you define a class that implements this interface. Now you have as much classes as you have listener interfaces.

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 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 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 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 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 .

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.

How can it be compiled

In order to compile NiceSwing it is the easiest do this using the AntBuildFile?. There are several targets to compile the various (independent) parts from NiceSwing in it. Currently everything is placed in package nice.ui.common . This is due to a not yet solved issue concerning abstract interfaces and packaging . As soon as nicec will allow to compile NiceSwing with the originally intended package structure, it will be possible to build the three parts (nice.ui.beans nice.ui.awt and nice.ui.swing) separately. I added an updated AntBuildFile? to CVS that defines two new targets to help the current packaging situation (compile-common and deploy-common).

When working on swinglib itself and deploying it in jar files, it is necessary to manually add each package.nicei file (there's one in each package) to the jar archive. If this is left out nicec will not be able to recognize the jar file as a library. This generally applies to any jar file that is intended to be a library.

Project Information

How is it maintained

Currently I (MartinGamsjaeger?) am the only maintainer of NiceSwing. Whenever I can find some time I will add the few things that are still missing (The retyping section is not complete yet). However, anyone interested to contribute in any way is welcome to contact me!

How can I help

First of all: USE IT and report any bugs to me! There is currently no project that would be a stress test for the library, so I would be very glad to see anyone using NiceSwing in a real life application. Except from using the library, everyone is invited to look at the source, see where it could be enhanced, find (and correct) bugs, make suggestions and come up with feature proposals. I am open to any idea to enhance the NiceSwing library!

Future Plans

The retyping section will have to be completeted. When this is done all (current) goals are met. As I will start working on an EclipsePlugin? soon, it is very likely that support for the SWT toolkit will be added. This will allow to do event handling in SWT in the same manner as described here for Swing and AWT.

-- MartinGamsjaeger

----- Revision r1.1 - 24 Apr 2003 - 22:02 GMT - DanielBonniot
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.