Programmer's Manual
Programming experience with NeuroKernel is meant to be easy and simple without any bloat. All system interfaces are easily accessible. It is designed after the popular event based model so that the learning curve will be short for developers familiar with event based programming such as Swing and SWT.
1. NeuroKernel Application
NeuroKernel applications always extend NApplication
class to execute, and main method must be overridden. It is possible to configure an application with annotations that comes with the API. Annotations may also be used to reduce the code size and generate deployment files for the compiled application when the target is also the client side. An application can be compiled for both server and client side without changing a single line of code. Only thing to figure out here is that to see if the supported Java packages are available from the selected transpiler.
1.1 Execution Entry Point
NeuroKernel application entry point is the main method overridden from the NApplication
class. The main method of a NeuroKernel application should not be confused with static main method of Java application. NeuroKernel also needs to use static main method in some scenarios.
import com.neurokernel.client.*; public class HelloWorld extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); new NButton(mainFrame,"Hello").addListener(event -> NMessageDialog.showInfo(mainFrame,"Hello World"), NEventTypes.ACTION); setVisible("Hello"); } }
As seen from above it is pretty similar to Swing and SWT style programming which also would help legacy applications to be easily ported to NeuroKernel.
public class MyApplication extends NApplication { @Override public void main(int argc, String[] argv) { } }
Here, argc
is the number of arguments and argv
is the array of arguments similar to C programming language to distinguish it from the standard Java main method.
2. Application Main Frame
When an application execution request reaches Kernel, the task processor module asks window manager to allocate an application main window. This top level window is the root to all other windows, dialogs or tool windows created by the application. Windows and controls are created in a tree hierarchy where the top level main window is the root of all. Destroying the main window means exiting the application entirely. Applications can reach the main window by calling getMainFrame
method. This method returns a NFrame
class instance which lets configuring its decorations. NFrame
class is a direct sub class of NWindow
.
3. Accessing System Interfaces
Developers can access system interfaces provided by the task container using getSystem
method of NApplication
class. Classes that extends NContainer
class will also able to access getSystem
method directly for rapid development.
import com.neurokernel.client.*; import com.neurokernel.system.ISystem; import com.neurokernel.adapter.IActionListener; public class SystemInterfaceExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); new NButton(mainFrame,"Get Name").addListener((IActionListener) e -> { ISystem system=getSystem(); //from NApplication class String appName=system.getApplicationName(); String codeBase = system.getCodeBase(); // from NContainer class NMessageDialog.showInfo(mainFrame, appName+" "+codeBase); }); mainFrame.setTitle("System Interface"); mainFrame.setBounds(20,20,200,200); mainFrame.setVisible(true); } }
4. Reading Resources
Resources can either be embedded to the executable code using LoadResources
annotation or can be loaded from a remote resource or service. The getResource
methods can be accessed from NApplication
class. It is also possible to set the application icon image using loadIcon
method. Task container will try to load the application icon automatically; however, this behaviour can be turned off using the LoadResources
annotation.
import com.neurokernel.client.*; import com.neurokernel.system.annotation.LoadResources; import com.neurokernel.system.annotation.Source; @LoadResources( resources = { @Source(value = "resources/myicon.gif", name = "appicon") @Source(value = "resources/banner.png", name = "banner"), }, loadIcon = false ) public class ResourcesExample extends NApplication { @Override public void main(int argc, String[] argv) { loadIcon((NImage)getResource("appicon")); new NImageView(getMainFrame(), (NImage)getResource("banner")); setVisible("Resources"); } }
4.1 Images
The class used for image references in NeuroKernel is NImage
. It implements IPixMap
interface which means it can be also be used when drawing graphics. Adding an image resource to a NeuroKernel application is quite easy. LoadResources
annotation is used to embed images to the final executable. If the target is only for the Java byte code then adding images to the resources folder in your application Jar file will be just sufficient. When using the JavaScript or WebAssembly as a target, generators are used to embed the image resource to the final executable. If a specific transpiler is to be used for an entire project, it may be easier to use the supplied way of loading resources by the transpiler without going through the generators. NImage
uses various ways to create image references which may be seen in the Javadoc reference.
4.2 Raw data
Loading raw data including text is not different than loading image files. Generators decide how to store the data in the binary if the compilation target is not Java byte code.
5. Localization
System default localization interface object can be accessed using getLocalization
method of NApplication
and NContainer
classes. It is also available from ISystem
interface. The method will return an ILocalization
interface object. Component layout orientation is determined by the current locale used although it can be set manually.
import com.neurokernel.client.*; import com.neurokernel.client.adapter.IActionListener; import com.neurokernel.system.ILocalization; public class LocalizationExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); new NButton(mainFrame,"Get Date").addListener( (IActionListener) e -> { ILocalization locale=getLocalization(); //from NApplication class String date= locale.getDateFormat().getDateString(System.currentTimeMillis(), true); NMessageDialog.showInfo(mainFrame, date); }); setVisible("Localization Interface"); } }
6. Using UI Controls
NeuroKernel API has a rich set of UI controls to rapidly prototype an application. The controls can be created and added to a layout instantly. The top most level abstract class of all controls is the NControl
class. Control class branches into components and cells. Containers extend the NComponent
class. All toplevel window controls and containers extend the NContainer
class. Cells are simple yet powerful controls that can take only NColumn
as its parent. NCell
is the abstract super class of all cells. A more detailed description and types of UI controls please see the User Interface Controls.
7. Event Handling
NeuroKernel is an event based system. Input/Output, Service calls, System calls and UI controls all use the event based infrastructure of NeuroKernel. Most system events can be listened using IDataListener
interface which is generally the expected interface by the corresponding methods. UI controls expect IEventListener
as the listener. It is a generic UI event listener which is specialized using event adapters. Developers are free to make further specialization by implementing the IEventListener
. UI controls also offer another listener type called observer. Observers only purpose is to get the current value of the UI control. It is possible to write a bean framework based on the observer features of the UI controls.
import com.neurokernel.client.*; import com.neurokernel.adapter.IActionListener; import com.neurokernel.client.constants.*; public class EventListenerExample extends NApplication { private NFrame mainFrame; private NSlider slider; @Override public void main(int argc, String[] argv) { mainFrame = getMainFrame(); NGridLayout gridLayout=mainFrame.getLayout(); gridLayout.setSize(3,1); NTextField textfield = new NTextField(mainFrame); slider = new NSlider(mainFrame, Orientation.HORIZONTAL, 0, 255, 0); slider.addObserver(textfield); new NButton(mainFrame,"Get Value").addListener((IActionListener) e -> NMessageDialog.showInfo(mainFrame, slider.getValue())); mainFrame.setTitle("Listener/Observer Example"); mainFrame.show(20,20); } }
Generic listeners as seen above can be used to efficiently handle all events fired on a UI control. This gives great control to the developer with less code. It is also possible to add custom Observers using IObserver
interface. All UI controls in NeuroKernel implement IObserver
interface by default.
import com.neurokernel.client.*; import com.neurokernel.system.io.*; import com.neurokernel.adapter.IActionListener; public class DataListenerExample extends NApplication { @Override public void main(int argc, String[] argv) { new NButton(getMainFrame(), "Get Date").addListener((IActionListener) e -> { NFile myFile = new NFile("/myfile.txt"); IFileSystem fileSystem=myFile.getFileSystem(getSystem()); fileSystem.exists(myFile, response -> NMessageDialog.showInfo(getMainFrame(),response.hasError() ? "File does not exists" : "File exists"); }); setVisible("IDataListener Interface"); } }
In the case of data listeners, it is also very straight forward to use them like event listeners. Above example, checks if a file exists in the default file system or not. As you see from the above example, system determines the file system of the file automatically by going through the currently mounted file systems. Developers can mount their own file systems if they wish. The addListener
method can use any listeners implementing IEventListener
interface as we have mentioned in this section above.
8. Layout Management
Every container in NeuroKernel API has a layout manager which can be accessed with getLayout
method of NContainer
class. NLayoutManager
is the abstract super class of all layout managers accepted by containers. NeuroKernel API comes with ready to use layout managers such as grid layout manager. Developers can write their own layout managers if needed. It is also possible to port Swing or SWT layout managers with ease. First thing to note in the below example, components are automatically placed on the layout to make prototyping faster. It is possible to rearrange the order with various methods available through the base layout manager class.
import com.neurokernel.client.*; public class GridLayoutExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame = getMainFrame(); NGridLayout grid = mainFrame.getLayout(); grid.setSize(2,2); // 2x2 grid new NButton(mainFrame,"1"); new NButton(mainFrame,"2"); new NButton(mainFrame,"3"); new NButton(mainFrame,"4"); mainFrame.setTitle("Layout Manager"); mainFrame.setBounds(20,20,200,200); mainFrame.setVisible(true); } }
NGridLayout
is a very optimized layout and gives more with less coding. Sub grid layout definitions eliminate nested panel creation to sub layouts. When a layout of a container is set to null using setLayout
method, NCoordinateLayout
is automatically assigned which requires absolute positioning of the components in the container.
9. Windows and Dialogs
Windows and dialogs in NeuroKernel are designed to be similar with modern desktop systems. Dialogs only block their immediate parent window. It is also possible to open nested dialogs. Window manager handles them efficiently in kernel level. Tool windows are also available which always stay on top of their immediate parent window. Dialogs can not have tool windows. Window system is one of the most advanced features of NeuroKernel.
import com.neurokernel.client.*; import com.neurokernel.adapter.*; public class DialogExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); new NButton(mainFrame,"Hello!").addListener((IActionListener) e -> new AskQuestion(getMainFrame()).setVisible(true)); mainFrame.setTitle("Hello There"); mainFrame.show(20,20); } /** * Command Dialog Example */ static class AskQuestion extends NCommandDialog { public AskQuestion(NWindow parent) { super(parent, "Enter Name", false); NContainer content=getContentPane(); content.getLayout().setPadding(5, 0, 5, 0); NTextField name = new NTextField(content); name.setPromptText("Enter Your Name"); getOKButton().getForm().addField(name); addDialogListener(e -> NMessageDialog.showInfo(getParentWindow() ,name.getText())); setSize(300, 80); } } }
In the above example, a command dialog is created which has cancel and okay buttons by default. All components in NeuroKernel are keyboard accessible. For instance, return key means pressing okay button with this dialog. As seen in this example, with using very little code, a lot can be done.
import com.neurokernel.client.*; public class MyApplication extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); mainFrame.setTitle("Tool Frame Example"); mainFrame.setBounds(20,20,300,300); mainFrame.setVisible(true); NToolFrame toolA = new NToolFrame(mainFrame, "Sticky"); toolA.setWindowSticky(true); toolA.setBounds(30, 30, 250, 250); toolA.setVisible(true); NToolFrame toolB = new NToolFrame(mainFrame, "Always on Top"); toolB.setAlwaysOnTop(true); toolB.setBounds(50, 50, 200, 200); toolB.setVisible(true); } }
Child windows can only be painted if their parent is painted and ready. The parent does not need to be visible, but must be painted at least using paintComponent
method. System automatically paints the window if setVisible
method is called.
9.1 MDI vs Single Window
NeuroKernel API has multi document support if some use cases would need it. NDesktopPane
is the container that holds the internal frames. It is possible to manage the internal frames differently by extending the super class of NDesktopPane
which is NDesktopView
. Internal frames function similar to normal windows.
import com.neurokernel.client.*; public class MDIExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame=getMainFrame(); NDesktopPane pane=new NDesktopPane(mainFrame); NInternalFrame frameA=new NInternalFrame(pane); new NButton(frameA,"Hello"); frameA.setBounds(5,5,200,200); NInternalFrame frameB=new NInternalFrame(pane); new NButton(frameB,"World"); frameB.setBounds(15,15,200,200); mainFrame.setTitle("MDI Example"); mainFrame.setBounds(20,20,400,400); mainFrame.setVisible(true); } }
9.2 Popup Menu
A popup menu in NeuroKernel are actually a type of popup window which is one of the top level window elements but requires a parent window to hold its place in the hierarchy. A custom menu can be crafted by extending NPopupWindow
class although API offers several sub classes which are readily configured.
import com.neurokernel.client.*; public class MenuExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame = getMainFrame(); mainFrame.setLayout(new NFrameLayout(NFrameLayout.MENU_BAR)); NMenuBar menubar = new NMenuBar(mainFrame); NMenu menu = new NMenu(toplevel); new NMenuCell(menu, "Open"); new NMenuCell(menu, "Exit").addSeparator(); menubar.addMenu("File", menu); menu.getColumn().addListener(event -> { if (event.getEventType() == NEventTypes.ACTION) { if (event.getRow() == 0) { NMessageDialog.showInfo(mainFrame,"Open a File?"); } else { exit(); } } }); new NButton(mainFrame,"Hello").addListener(event -> NMessageDialog.showInfo(mainFrame,"Hello There!"), NEVentTypes.ACTION); setVisible("Menu Example"); } }
9.3 Column and Cells
Cells are simple controls that accept a column container as its parent. NeuroKernel API uses column and cells extensively in its container structures. Basically, NColumn
is the base class for all cell holding containers such as list or tree. Cells are added as single rows to a column. There are extensive set of methods that makes manipulating a column structure easier. Tables also use columns and cells.
import com.neurokernel.client.*; public class ColumnExample extends NApplication { @Override public void main(int argc, String[] argv) { NFrame mainFrame = getMainFrame(); mainFrame.getLayout().setPadding(5); NList list = new NList(new NScrollView(mainFrame)); list.addData("Data Cell 1","Data Cell 2","Data Cell 3","Data Cell 4", "Data Cell 5","Data Cell 6","Data Cell 7","Data Cell 8"); list.attachClientEvent(ClientEvent.POINTER_DOUBLE_CLICK); list.addListener(event -> NMessageDialog.showInfo(mainFrame, list.getSelectedCell().getValue()), NEventTypes.POINTER_DOUBLE_CLICK); setVisible("Column Example"); } }
9.4 Using Graphics
Graphics is one of the strong features of NeuroKernel API. It is effectively available even when the application run at the server side. It has a optimized protocol that makes caching of drawings at the display server possible for performance. Below example shows how a display list is used to cache the drawing. It is possible to turn a plain NComponent
into a drawable canvas similar to Swing although a more specialzed NCanvas
class is available for the purpose.
import com.neurokernel.client.*; import com.neurokernel.client.graphics.*; public class GraphicsExample extends NApplication { @Override public void main(int argc, String[] argv) { NComponent canvas = new NComponent(getMainFrame()) { IDisplayList mygraphics; @Override public void paint(IGraphics graphics) { IGraphics2D context = (IGraphics2D) graphics; if(mygraphics==null) { mygraphics=context.newList(); context.setStrokeStyle(new NColor(128, 128, 255)); context.setFillStyle(new NColor(0, 0, 255)); context.setLineWidth(5); double pi = 2 * Math.PI; context.beginPath(); context.arc(50, 50, 100, 0, pi, false); context.closePath(); context.fill(); context.beginPath(); context.arc(50, 50, 100, 0, pi, false); context.closePath(); context.stroke(); context.endList(); } context.translate(100, 100); context.callList(mygraphics); } }; setVisible("Drawing Graphics"); } }
NCanvas class is the preferred way of drawing graphics. It is possible to give an initial drawing to the canvas that can be cached as seen in the below example. Canvas class has features to utilize offscreen canvas as well if available. It can also double buffer drawing.
import com.neurokernel.client.*; import com.neurokernel.client.graphics.*; public class CanvasExample extends NApplication { @Override public void main(int argc, String[] argv) { NCanvas canvas=new NCanvas(getMainFrame()); IGraphics2D ctx = canvas.getGraphics(); ctx.setFillStyle(NColor.BLUE); ctx.rectangle(10, 10, 55, 50); ctx.fill(); ctx.setFillStyle(NColor.RED); ctx.rectangle(30, 30, 55, 50); ctx.fill(); setVisible("Canvas Example"); } }
10. Native Panel
Native panel is available when application is run at the client side. In server side runtime, it acts as a graphics canvas. Native pane can be used to create visual experiences using third party libraries. It also has the ability to hook NeuroKernel graphics interface to an HTML5 canvas element which makes direct manipulation possible without going through protocol layer. Example below shows the use of a HTML canvas element as a IGraphics
interface.
import com.neurokernel.client.*; import com.neurokernel.client.graphics.*; public class NativePaneExample extends NApplication { @Override public void main(int argc, String[] argv) { NNativePane nativePanel = createNativePane(getMainFrame()); IGraphics2D ctx = nativePanel.getGraphics(); ctx.setFillStyle(NColor.BLUE); ctx.rectangle(10, 10, 55, 50); ctx.fill(); ctx.setFillStyle(NColor.RED); ctx.rectangle(30, 30, 55, 50); ctx.fill(); setVisible("Native Pane); } }
11. Service Calls
Services at the server side is available using IServiceHandler
system interface. There are various options with this interface including making CORS calls. Result of the service class is listened using IServiceListener
interface as seen below.
import com.neurokernel.client.*; import com.neurokernel.system.net.*; public class ServiceExample extends NApplication { @Override public void main(int argc, String[] argv) { IServiceHandler serviceHandler = getSystem().getServiceHandler(); serviceHandler.callService("GetData", "John Doe", connection -> { NFrame mainFrame = getMainFrame(); if(connection.hasError()) NMessageDialog.showError(mainFrame, "Data is not found!"); else NMessageDialog.showInfo(mainFrame, connection.readString()); }); setVisible("Service Example"); } }
12. Terminal IO
NeuroKernel also offers an interface to input and output to a terminal. A terminal application is available with the default package which is used for terminal IO (Input/Output). The terminal access request is made with getTerminal
method which returns an ITerminal
interface object, and ITerminalListener
is used to manage the input and output operations. Application must be executed from the terminal command line in order to have terminal IO functionality. ITerminal
interface object is also passed to the listener methods for more convenient way of interaction.
import com.neurokernel.client.*; import com.neurokernel.system.io.*; import com.neurokernel.system.*; /** * This application must be run from NeuroKernel terminal */ public class TerminalExample extends NApplication { @Override public void main(int argc, String[] argv) { NLabel label=new NLabel(getMainFrame()); getSystem().getTerminal(stdio -> { String input=stdio.readString(); if (input == null && stdio.getKeyCode() == 0) { stdio.writeString("Hello Console!"); stdio.setPrompt("password:", true); } else if (stdio.getKeyCode() > 0) { stdio.setPrompt("Command> "); } else { stdio.writeString("You have entered: " + input); if (input.equals("close")) { stdio.close(); } else { label.setText(input); //also sets the label text stdio.writeString("Press any key to continue...", NSystemConstants.TERMINAL_WAIT_KEY_INPUT); } } }); setVisible("Terminal Example",20,20,300,300); } }
13. Exiting Application
Exiting an application can be done by calling the exit method of NApplication
class, or disposing the application main window. It is possible to kill an application from terminal command line as well.
import com.neurokernel.client.*; public class ExitExample extends NApplication { @Override public void main(int argc, String[] argv) { new NButton(getMainFrame(),"Exit").addListener(e -> exit()); setVisible("My Application"); } }