пятница, 27 августа 2010 г.

ListView Multiple Selection 2

After reading the post about ListView Multiple Selection I started integrating a new ListView it into my project, but in few minutes I figured out that there were no normal key multiple selection support. So, I decided to fix this problem.

The problem

At first I was thinking about overriding callActionForEvent(e:KeyEvent):Void method in com.sun.javafx.scene.control.behavior.ListViewBehavior, but I've found out that it's not so simple. After applying a new behavior for a ListView in ListViewSkin like this:
class ListSkin extends ListViewSkin {
     override var behavior = ListBehavior{}
     ...
}
The ListView stops scrolling up/down if I use keys for navigation. I was thinking about putting some parameters in ListBehavior, but still no luck.

Workaround

As for workaround you'll need to put some extra code in MultiSelectListView class
override var onKeyPressed = function(e:KeyEvent):Void {
if ((e.code == KeyCode.VK_UP) or (e.code == KeyCode.VK_DOWN)
        or (e.code == KeyCode.VK_PAGE_DOWN) or (e.code == KeyCode.VK_PAGE_UP)
        or (e.code == KeyCode.VK_HOME) or (e.code == KeyCode.VK_END)) {
        if (not e.controlDown and not e.shiftDown) {
           delete selectedIndexes;
        }            
    }
    if (e.controlDown and e.code == KeyCode.VK_SPACE) {
        def index:Integer = (e.node as List).focusedIndex;
        if (-1 == Sequences.indexOf(selectedIndexes, index)) {
            insert index into selectedIndexes;
        } else {
            delete index from selectedIndexes;
            if (index == selectedIndex) select(selectedIndexes[0]);
        }
    }
}

As a result

  • So, now you are able to use Ctrl+Space to add new selected items or remove old items from selection.
  • If you use keys such as: HOME, END, PAGE_UP, PAGE_DOWN, UP or DOWN without holding a Ctrl button multiple selection will be lost(I think it's user expected behavior :) )
  • I didn't do anything with Shift key. So, it does nothing.

Small fix

The ListView by Jonathan Giles has a small bug, it's also about using keys for selecting items. In MultiSelectListCellBehavior class, he has a case for deselecting all previously selected items.
public class MultiSelectListCellBehavior extends ListCellBehavior {
    ...
    override function mouseReleased(e) {
       ...
       } else {
           delete listView.selectedIndexes;
           insert row into listView.selectedIndexes;
           listView.focus(row);
       }
    }
}
As you can see in the end he used listView.focus(row); for selecting an item, but this stuff wont wok correctly. If you made some selection with the help of keys you'll get at least two selected items. So, I think it's better to use listView.select(row); instead.

Have a nice code :)

понедельник, 23 августа 2010 г.

Creating Pseudo-class in JavaFx (TabPanel control)

Before reading this article I suggest to read Advanced JavaFX Control Styling by Dean Iverson.
After JavaFx 1.3 has been published we've got a new bunch of controls and a new way to produce our own ones and, of course, don't forget about new CSS support in this release. So, today I'm going to show you how to create your own JavaFx controls with CSS support.

Specification

Currently there are no tabs in JavaFx, and that's a good reason to start having them :). At the end I expect to get such a result:

Specification itself:

  • TabPanel has TabButtons
  • You are able to select only one TabButton in TabPanel
  • You are able to set styles for TabButtons and TabPanel
  • TabButtons can be aligned inside TabPanel
  • TabButtons can fit TabPanel by width, height or both
  • TabPanel can set common width or height for TabButtons

Implementation

I'm not going to explain all the code, but I'll stop in most interesting parts:

Creating your own pseudo-class

Each TabButton should have a status selected in order to display the button as selected and not to perform a click action on it.
public class TabButton extends ButtonBase, Labeled {
    
    override var skin = SkinAdapter {
        rootRegion:ButtonSkin{}
    }

    public var selected:Boolean = false on replace {
        impl_pseudoClassStateChanged("selected");
    }

    override function impl_getPseudoClassState():String[] {
        return [
            if (selected) "selected" else null,
            super.impl_getPseudoClassState()
        ]
    }
}
This is just a piece of the TabButton class. As you can see, I use two functions impl_pseudoClassStateChanged and impl_getPseudoClassState that will help me to organize my pseudo-class &quote;selected&quote; for this control.
Now let's take a look at the CSS file:
.tab-button {
    -fx-padding: 3 10 2 10;
    -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color;
    -fx-background-radius: 10 10 0 0;
    -fx-background-insets: 0 0 -1 0, 0, 1, 2;
    -fx-border-radius: 10 10 0 0;
    -fx-border-color: -fx-outer-border, -fx-inner-border;
    -fx-border-style: solid;
    -fx-text-fill: -fx-text-base-color;
    -fx-vpos: CENTER;
    -fx-graphic-vpos: CENTER;
}

.tab-button:hover {
    -fx-color: -fx-hover-base;
}

.tab-button:selected {
    -fx-color: -fx-pressed-base;
}
Please pay attention, that now I can use keyword selected as a name of the new pseudo-class for my control. Also I can use standard pseudo-classes of Button and Labeled controls cause I invoke a super.impl_getPseudoClassState() highlighted on line 14.
Now look at line 4, in order not to write my own Skin class, for this control, I'm using an existing one.

Source

To download a full source code press here

Have a nice code :)

четверг, 24 июня 2010 г.

Binding Java model in JavaFx

All this time I was looking for a good solution of binding java model class to javafx UI. There are some articles about that and the most interesting, on my mind, is on Mike's Blog, but it's seams like a lot of code to write and some of the examples are not working at all. There are also some frameworks for binding through aspects, but still it wasn't the way I want it to be.
So, I think I found a nice solution, that is really easy to implement and to use.


Making interfaces

Let's make an interface for our future model. No rocket-science just getters and setters.

public interface TextModel {
     public void setText(String text);
     public String getText();
}

Creating a JavaFx model

There are some restrictions for using binding in javafx. One of them is that you can't pass a method in bind. for example:

Label {
    text:bind model.getText() // wont work
}

For this reason we are going to create a javafx class, that implements our new interface and has a public field. It will helps us to use all the good binding stuff of javafx. After all we going to have a class that has a javafx property, that can be changed directly form java

public class JFxTextModel extends TextModel {
     public var text:String;
     public void setText(String text) {
         this.text = text;
     }
     public String getText() {
         return this.text;
     }
}

For newbies in JavaFx, there is no implements option for implementing interfaces, we use extends

Now we can move all our business login on Java side and leave only views and model as a bridge on JavaFx side

def model:JFxTextModel = JFxTextModel{};
Label {
    text:bind model.text
}

P.S.

It's really useful to have getters in interfaces, cause you can retrieve data on Java side, leaving javafx only for displaying and effect.

Have a nice code :)

понедельник, 22 февраля 2010 г.

Simple-XML with OSGi

For me these weekends was almost a final step of our project transformations with OSGi and of cause there were a lot of problems. One of these problems was that there are libraries that don't have bundle representation for OSGi systems. Thanks God, for me it was only the one library: Simple-XML. So I had to recompile it using maven-bundle-plugin from Apache.


Steps to do it yourself:

  1. Download sources from here
  2. Write a new pom.xml and package a new bundle

Here is my pom.xml for this library:

<project 
 xmlns="http://maven.apache.org/POM/4.0.0"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.simpleframework</groupId>
  <artifactId>simple-xml</artifactId>
  <version>2.3</version>
  <packaging>bundle</packaging>
  <name>Simple XML</name>
  <url>http://simple.sourceforge.net</url>
  <description>Simple is a high performance XML serialization and configuration framework for Java</description>
  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  
  <build>
   <sourceDirectory>src/</sourceDirectory>
<finalName>${project.groupId}.${project.artifactId}-${project.version}</finalName>
   <plugins>
    <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-jar-plugin</artifactId>
   <configuration>
    <archive>
     <addMavenDescriptor>false</addMavenDescriptor>
    </archive>
   </configuration>
  </plugin> 
    <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-compiler-plugin</artifactId>
   <configuration>
    <encoding>UTF-8</encoding>
    <source>1.5</source>
    <target>1.5</target>
   </configuration>
  </plugin>
    <plugin>            
   <groupId>org.apache.felix</groupId>
   <artifactId>maven-bundle-plugin</artifactId>
   <extensions>true</extensions>
   <configuration>
     <instructions>
      <Export-Package>org.simpleframework.xml.*</Export-Package>
    <Private-Package>org.simpleframework.xml.*</Private-Package>
     </instructions>
   </configuration>
  </plugin>
   </plugins>
  </build>
  
 <dependencies>
  <dependency>
       <groupId>stax</groupId>
       <artifactId>stax-api</artifactId>
       <version>1.0.1</version>
       <scope>compile</scope>
     </dependency>
     
     <dependency>
       <groupId>stax</groupId>
       <artifactId>stax</artifactId>
       <version>1.2.0</version>
       <scope>compile</scope>
     </dependency>
     
     <dependency>
   <groupId>org.osgi</groupId>
   <artifactId>osgi_R4_core</artifactId>
   <version>1.0</version>
   <scope>provided</scope>
   <optional>true</optional>
  </dependency>
  <dependency>
   <groupId>org.osgi</groupId>
   <artifactId>osgi_R4_compendium</artifactId>
   <version>1.0</version>
   <scope>provided</scope>
   <optional>true</optional>
  </dependency>
 </dependencies>
</project>

As you can see I've removed all the tests from my pom.xml. If you want to add tests to this project it's important to edit a testSourceDirectory in the same way as I did it with sourceDirectory cause by default maven search for src/main/java and sources for this project is just in src


Downloads

For those who don't want to mess with all this recompilation stuff here are links to download this bundle from my google-docs


Installation

After you download it or do it yourself you'll need to install it in your local or companies repository. To do so simply invoke this line of code in your shell
mvn install:install-file -DgroupId=org.simpleframework -DartifactId=org.simpleframework.simple-xml -Dversion=2.3 -Dpackaging=jar -Dfile=org.simpleframework.simple-xml.2.3.jar


Usage

In your maven project you'll need to add these lines:

<dependency>
 <groupId>org.simpleframework</groupId>
 <artifactId>org.simpleframework.simple-xml</artifactId>
 <version>2.3</version>
</dependency>

P.S.

During all the time I was transfering my project to OSGi world I used THE GREATEST MAVEN BUNDLE REPOSITORY

Have a nice code :)