Sunday, July 7, 2013

Create a Notepad with Javafx2 - Accelerators and MnemonicParsing

Our Notepad is sufficient with functionalities we have added. But I can still feel there is something missing. Something that we should add to our notepad to make it complete. What about adding the Mnemonics? What if our user presses the combination of ALT button and a specified Key and the File menu opens up automatically. Yes, this is definitely required. 

How to achieve this? Is is complicated? Well, no. As a matter of fact this is very easy. Open you notepad.fxml and update the following.

<Menu text="_File" fx:id="menufile" mnemonicParsing="true">
<Menu text="_Edit" mnemonicParsing="true">
<Menu text="F_ormat" mnemonicParsing="true">
<Menu text="_View" mnemonicParsing="true">
<Menu text="_Help" mnemonicParsing="true">

Looks pretty simple right? All that you have to do is add mnemonicParsing="true" wherever required. Now, for the key that you would want to associate with the ALT key all that you have to do is put an  "_" before that letter in your Menu text as shown above. For example if you want your File Menu to be opened up when you press ALT + F then change the text for File Menu from "File" to "_File". If you want Format Menu to be opened on ALT+O then change "Format" to "F_ormat".
That's it. The Mnemonics have been set. Now, run your application and test.

While the Mnemonics have been set and are paying a very important user friendly activity for us, but we still need a little more than this. There are some additional features that a notepad should have for its full usage. What are these? Lets say you are typing on the notepad and you feel like saving your work. Normally you'll do a Ctrl+S and it works for you. But here we have not provided anything that helps with this. Lets try and add this feature to our notepad as well. Now this may sound tricky but trust me it's not. This can easily be achieved through Accelerators. 

Let's start with this. Open your fxml file. Now, wherever you want an accelerator defined update you code as shown below.

<MenuItem fx:id="newItem" text="New" onAction="#newFile"></MenuItem>

All you are doing here is adding an ID to your Menu Item.

Now open you controller class and update it so that it implements the "javafx.fxml.Initializable" interface. The method that you'll have to implement would be 

@Override
public void initialize(URL location, ResourceBundle resources) {


}

Simple. This is called before the fxml is loaded and hence used to initialize your screen. 

Now add a global parameter that defines the menu item you have given ID's to.
@FXML
private MenuItem newItem; 

So far so good. Now you have got the reference of your MenuItem. For me it is the New MenuItem in FileMenu.

Now,  add the following code in your initialize method.

KeyCombination kc = new KeyCodeCombination(KeyCode.N,
KeyCombination.CONTROL_DOWN);

newItem.setAccelerator(kc);


The above code declares a KeyCombination class. Here you define the key combinations. In the example above you have added Ctrl + N as Key Combination. next you add the combination as an accelerator to the menuitem   instance. 

That's it. Now Ctrl +N will also call the onAction method defined on New Menu Item. 

Your shortcut is ready. These definitely are a lot helpful in using your applications and hence should be used wherever you can.  

Notepad is finished. There were a lot of things that could have been done here but I did not because it was not worth discussing here. For example, adding  a message box when you press New in File Menu asking the user if he actually wants to save his work or not. I did not add the functionality of any other Menu like Edit. Trust me these are essentials for out application but need not be discussed here. The motive here was to examine the ease with which the applications like notepad be developed using JavaFX. 

If you want to visit my prev blogs on notepad, please click on the links below.
Create a Notepad with Javafx2 - Building the Logic
Create a Notepad with Javafx2 - Styling
Create a Notepad with Javafx2 - The Layout

Saturday, July 6, 2013

Create a Notepad with Javafx2 - Building the Logic

Notepad is a simple application. I am trying to demonstrate how easy creating the logic could be, by using Javafx2. The previous two blogs helped us in creating the layout and styling the notepad
In this blog we will try to create the logic for our notepad to work. This would deal with adding functionalities that would be triggered for specific needs.

Lets start by adding the methods for File Menu.

This is simple. Open your notepad.fxml and update the File Menu code as under.

<items>
<MenuItem fx:id="newItem" text="New" onAction="#newFile"></MenuItem>
<MenuItem text="Open..." onAction="#openFile"></MenuItem>
<MenuItem text="Save" onAction="#saveFile"></MenuItem>
<MenuItem text="Save As..." onAction="#saveasFile"></MenuItem>
<SeparatorMenuItem></SeparatorMenuItem>
<MenuItem text="Page Setup..."></MenuItem>
<MenuItem text="Print"></MenuItem>
<SeparatorMenuItem></SeparatorMenuItem>
<MenuItem text="Exit" onAction="#exitApp"></MenuItem>
</items>

onAction defines the methods that would be called when you click on the respective Items.
These methods will be defined in the controller of the fxml. Defining a controller is simple. Add this to the place you defined BorderPane.

<BorderPane xmlns:fx="http://javafx.com/fxml" fx:controller="NotepadController">

Easy, the fully qualified name of the class is defined here. Next, create a controller class with the name and package matching the fully qualified class name you have given above. Now that you have the controller created, add the methods defined above to your class.

import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;

public class NotepadController{

@FXML
protected void newFile(ActionEvent event) {
System.out.println("new File");
}
@FXML
protected void openFile(ActionEvent event) {
System.out.println("Open file");
}
@FXML
protected void saveFile(ActionEvent event) {
System.out.println("Save File");
}
@FXML
protected void saveasFile(ActionEvent event) {
System.out.println("Save File As");
}
@FXML
protected void exitApp(ActionEvent event) {
System.out.println("Exiting");
}

}

The code above is very simple. It is just a class with methods defined. Observe the signatures of the method. This is the only thing to be noted down. Now your controller is ready. Lets start adding the functionality.

I start by adding the close event. 
Add a close Application code in the exit Event. Just update your code as shown below.

@FXML
protected void exitApp(ActionEvent event) {
Platform.exit();
}

Run your App and test exit event.  It should close the application as expected.

Now, lets add the new Functionality. When a user clicks on new the TextArea should be refreshed. This should be simple as well. All you need is a reference of the textArea in your controller. This is done in two steps.
1. Add an ID to textarea in your fxml file

<center>
<TextArea fx:id="textpane">
</TextArea>
</center>
2. declare ID in controller
@FXML
private TextArea textpane;

Now, that you have the reference of textarea add the desired functionality.

@FXML
protected void newFile(ActionEvent event) {
textpane.clear();
}

This was simple. Now lets start building the notepad.
First we start with the new File functionality.

@FXML
protected void newFile(ActionEvent event) {
textpane.clear();
Stage stage = (Stage) textpane.getScene().getWindow();
stage.setTitle("Untitled - Notepad");
file = null;
}

We have also defined two global parameters.

private FileChooser fileChooser = new FileChooser();
private File file;

The code is extremely simple. we cleared off the textarea. Then we changed the name of the window title. Third the file parameter was changed to null.

The code for Opening a file.

@FXML
protected void openFile(ActionEvent event) {
file = fileChooser.showOpenDialog(null);
if (file != null) {
Stage stage = (Stage) textpane.getScene().getWindow();
stage.setTitle(file.getName() + " - Notepad");
BufferedReader br = null;
try {
String sCurrentLine;
br = new BufferedReader(new FileReader(file));
while ((sCurrentLine = br.readLine()) != null) {
textpane.appendText(sCurrentLine + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

It is simple, open a file using a jfilechooser. Traverse it and append the same in the textpane. 

The code for saving a file 

@FXML
protected void saveFile(ActionEvent event) {
String content = textpane.getText();
if (file != null) {
try {
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
// open a file dialog box
file = fileChooser.showSaveDialog(null);
if (file != null) {
Stage stage = (Stage) textpane.getScene().getWindow();
stage.setTitle(file.getName() + " - Notepad");
try {
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

@FXML
protected void saveasFile(ActionEvent event) {
file = fileChooser.showSaveDialog(null);

String content = textpane.getText();
if (file != null) {
Stage stage = (Stage) textpane.getScene().getWindow();
stage.setTitle(file.getName() + " - Notepad");
try {
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}


The code for saving files is redundant and can be further optimized. But that is not the motive here. You can correct the code as you like. There can be many ways of doing the same thing and I don't deny that, but, keeping it simple is easier to understand.
The above code is not complicated as it may look, thanks to the formatting etc. readily available for usage here, all that is relevant here is setting the stage title and extracting the file information from the user. There are two important things for us how we get the stage from any element and the dialog boxes that we have on save and open. 

This completes our File menu related functionality for the notepad except for the print related tasks. I tried looking for JavaFX printing support but could not find one. This is still to be released in future editions. Though, we have alternatives for this but that goes beyond scope.

We know now how simple creating logic can be using JavaFX.
Apart from this lets move to other stuffs in the next and the final blog. Let us explore how Accelerators and Mnemonics be used.


Create a Notepad with Javafx2 - Styling

We created the layout of the notepad in the previous blog. We got a default look and feel of the application we are trying to create. Surely, we will need much more than that to impress ourselves with the capability of Javafx2. How did we style in Swing Applications? Or AWT's ? One of the advantages of using Javafx2 was the styling using the css. Even though I am not much of a css loving developer, still I thought of giving this a try and to my amazement the results were a bit more satisfying. 

Of course, it is up to the developer, but what I feel is that the idea of separating the presentation entirely makes a lot more sense than including the same in your code. Imagine a fully grown complex Application and all of a sudden "I kinda wanna change the way it looks"...

Now, lets try and make our application aware of the css. This is simple handled in the code. 
Open your Launcher.java or any other class where you have called your stage and scene instances. 
Add the line of code.

scene.getStylesheets().add(getClass().getResource("/css/notepad.css").toExternalForm());

I have an eclipse structure. My notepad.css should be residing in the src/css folder. To start with the basics you can read this css tutorial on Javafx2. This mentions about the the default css called "caspian.css" that is available in the   "jfxrt.jar". Even if you do not want to alter the css, it is a very good resource for your knowledge on the subject.

Now let us create a notepad.css in the mentioned folder structure. 
First, I specifically do not like the black colour of the menubar. Let's start by changing the colour of the same.
This is done in two steps.

Open your fxml file and an ID to the menubar.
<MenuBar fx:id="menubar">

Now add the following in your css
#menubar{
-fx-background-color: #b0c4de;
}

This is what you get as an output. 



















The colour is changed but it is still flat. Let us add a gradient look to it.
Change the above code to

-fx-background-color: linear-gradient(#FFF, #D4DBED, #E1E6F6);

It works well, but there is still lot of things to be fixed. Again, I am no css expert and hence not getting into too much detail here. Please refer css reference for details.

Moving ahead, I am not liking the white color of the menu labels. Let me change the same to black. 
Add the following code to the css file.
.menu .label{
    -fx-text-fill: black;
}
Here I am telling the fx engine to paint black all the labels of the menu.

Now, I observe a blue coloured border around the textarea that we created. Lets remove the same. This is Javafx default.
Update the below line to your fxml file. We are adding an id to the textarea.
<TextArea fx:id="textpane">
</TextArea>

Now let's add the style in css. Add the following styling to the css.
#textpane{
-fx-background-insets: 0, 0, 1, 2;
}

Instead of adding an ID you can use .text-area as well. Totally upto you.But we'll be needing the ID we added in our application later on. So might as well use it.

Now, I want to change the drop down menu that appears when you click on File or Edit or any other Menu. I want the background color a bit different and solid. The code below is to be added for this in your css file.

.context-menu {
-fx-background-color: #F1F1F1;
-fx-border-color: black;
-fx-border-width: 0.5;
}

It gives a more familiar looking context menu. Now we have almost the look and feel we want for our notepad. There are still a lot that can be done here. Lets stop with the styling part here itself.

The core of the application is the functionality. Let us start by adding those in the next part.