This blog entry shows one how to create a special widget using InkScape and NetBeans Visual Library. This is part of my effort to create a Java-based implementation of MIT’s Scratch, which is implemented in Squeak Smalltalk.
Step 1 Create JScratch Block Graphics:
First I created the image using Inkscape (steps to follow), import the image to IDE, then create two LayerWidgets, one transparent, and another opaque. Add the image to the background LayerWidget, adn the Swing component to the front LayerWidget. Finally, use the BorderFactory.createEmptyBorder function to limit the size of the swing component at the front layer.
To create the Scratch blocks, I used InkScape, the Open Source SVG editor. Here are the steps I took to create the graphic:
1) Turn on Grid and Guide by selecting View->Grid, and View->Guide, respectively.
2) Use “Bezier Curve and Straight Line” tool to create outline
3) Use “Edit Path by Nodes” tool to edit the outline
4) Then fill the color and add the text. This is what the “Move … Steps” block look like. I pick this block to explain because this is the more complicated block than those which do not have an input field.
Step 2 Create JScratch Block Graphics:
Next, we need to use turn this graphic into a visual widget. To do so, I use NetBeans’ Visual Library API. To learn more on Visual Library API, go back to Step 5 and install the example code. For now, I am just going to assume that you have some understanding of how Visual Library API works.
How to create a widget that looks like a Scratch block.
Let’s focus on the input field, the empty box. Each cell in the grid is 10 by 10 pixel. So there are 60 pixels from the left edge of the block to the left edge of the input box, 60 pixels from the right edge of the block to the right edge of the input box.
In the same way, there are 10 pixels from the top edge of the block to the top edge of the input box, and 20 pixels from the bottom edge of the block to the bottom edge of the input box.
This code is from the JButtonWidgetTest class, included in the test.swing package in the Visual Library API sample code
--------------------------------------------------------------------
public class JButtonWidgetTest extends Widget {
private JButton button = new JButton ();
public JButtonWidgetTest (Scene scene) {
super (scene);
}
public JButton getButton () {
return button;
}
protected Rectangle calculateClientArea () {
return new Rectangle (button.getPreferredSize());
}
protected void paintWidget () {
button.setSize (getBounds ().getSize ());
button.paint (getGraphics ());
}
public static void main (String[] args) {
Scene scene = new Scene ();
scene.getActions ().addAction (ActionFactory.createZoomAction ());
JButtonWidgetTest button = new JButtonWidgetTest (scene);
button.getButton ().setText ("My Button");
scene.addChild (button);
SceneSupport.show (scene);
}
}
To run this sample, just right click the JButtonWidgetTest.java in the Project panel and select “Run File”.
The result looks just like a regular JButton, but it’s a Widget that contains a JButton.
This is the simplest way of turning a Swing component to a Widget, but it can only contain one component. To do more, use ComponentWidget. To see ComponentWidget working in action, run ComponentTest.java (right-click and select “Run File”).
The result is a widget that contains a shell that says “Drag this ..” and a swing component.
This is how to create a ComponentWidget instance. ComponentWidget componentWidget = new ComponentWidget (scene, new JComboBox (new String[] { “First”, “Second”, “Third” })); For Scratch blocks, we can use ComponentWidget class to wrap a custom Swing component. To aid the creation of more complex blocks, I added an enum class called ScratchWidgetAttribute.
The ScratchPanelWidet class is very simple and its constructor takes a ScratchWidgetAttribute argument.
public enum ScratchWidgetAttribute {
MOVE_STEPS("text", 10, 60, 20, 60 ),//top, left, bottom, right
TURN_CLOCK("turn-clockwise",10, 60, 20, 60);
private String imagePath;
private int emptyBoxTop, emptyBoxLeft, emptyBoxBottom, emptyBoxRight;
ScratchWidgetAttribute(String image, int top, int left, int bottom, int right){
imagePath = image;
emptyBoxTop = top;
emptyBoxLeft = left;
emptyBoxRight = right;
emptyBoxBottom = bottom;
}
public String imageName(){ return imagePath; }
public int emptyBoxTop(){ return emptyBoxTop;}
public int emptyBoxLeft() { return emptyBoxLeft;}
public int emptyBoxRight(){ return emptyBoxRight;}
public int emptyBoxBottom(){ return emptyBoxBottom; }
The ScratchPanelFactory is the singleton class that handle creating the complex Swing component that looks like a Scratch block. It’s quite straightforward to create widgets for those blocks that do not contain an input field, such as these:
But it’s a bit tricky when it comes to these blocks:
To do so, first create a transparent JPanel, then add a JTextField to this JPanel. Then wrap this foreground JPanel with a background JPanel. The background JPanel contains the Scratch block image we created in Step 2.1
public class ScratchWidgetFactory {
...
public static JPanel wrapInBackgroundImage(
JComponent component, Icon backgroundIcon, int verticalAlignment, int horizontalAlignment)
{
// make the passed in swing component transparent
component.setOpaque(false);
component.setBackground (Color.WHITE);
// create wrapper JPanel
JPanel backgroundPanel = new JPanel(new GridBagLayout());
// add the passed in swing component first to ensure that it is in front
backgroundPanel.add(component, gbc);
// create a label to paint the background image
JLabel backgroundImage = new JLabel(backgroundIcon);
// Set the size of both foreground and background to be the size of the icon
backgroundImage.setPreferredSize(new Dimension(backgroundIcon.getIconWidth(), backgroundIcon.getIconHeight()));
backgroundImage.setMinimumSize(new Dimension(backgroundIcon.getIconWidth(), backgroundIcon.getIconHeight()));
// align the image as specified.
backgroundImage.setVerticalAlignment(verticalAlignment);
backgroundImage.setHorizontalAlignment(horizontalAlignment);
// add the background label
backgroundPanel.add(backgroundImage, gbc);
backgroundPanel.setOpaque(false);
// return the wrapper
return backgroundPanel;
}
public static JPanel createScratchComponent(ScratchWidgetAttribute attribute) {
JPanel foregroundPanel = new JPanel(new BorderLayout(0, 0));
foregroundPanel.setBorder( BorderFactory.createEmptyBorder(
attribute.emptyBoxTop(), // top
attribute.emptyBoxLeft(),// left
attribute.emptyBoxBottom(),// bottom
attribute.emptyBoxRight() // right
));
foregroundPanel.add(new JLabel(""), BorderLayout.NORTH);
JTextField textfield = new JTextField(0);
textfield.setBounds(10, 60, 20, 60);
foregroundPanel.add(textfield);
return wrapInBackgroundImage(foregroundPanel, new ImageIcon(
ScratchPanelFactory.class.getResource("images/" + attribute.imageName() + ".png")));
}
...
}
Finally, the test code:
public class ScratchWidgetDemo {
public static void main (String[] args) {
Scene scene = new Scene ();
LayerWidget layer = new LayerWidget (scene);
scene.addChild(layer);
Widget scratch1 = new ScratchPanelWidget(scene,ScratchWidgetAttribute.MOVE_STEPS );
layer.addChild(scratch1);
Widget turnClock = new ScratchPanelWidget(scene,ScratchWidgetAttribute.TURN_CLOCK );
layer.addChild(turnClock);
scene.getActions().addAction (ActionFactory.createZoomAction ());
scene.getActions().addAction (ActionFactory.createPanAction ());
SceneSupport.show (scene.createView ());
}
public class SceneSupport {
...
private static void showEDT (Scene scene) {
JComponent sceneView = scene.getView ();
if (sceneView == null)
sceneView = scene.createView ();
show (sceneView);
}
public static void show (final JComponent sceneView) {
if (SwingUtilities.isEventDispatchThread ()){
showEDT (sceneView);
}
else {
SwingUtilities.invokeLater (new Runnable() {
public void run () {
showEDT (sceneView);
}
};
}
...
}
Download the complete source code bundle here.
Superb information here, ol’e chap; keep burning the midinhgt oil.
Thanks for that tutorial!
I would be very interested with downloading that source code bundle but unfortunately it is not available anymore.
Can you upload it another time, or maybe send them directly to me, I would be really gratefull.
Thanks in advance!
Thanks for that tutorial!
I would be very interested with downloading that source code bundle but unfortunately it is not available anymore.
Can you upload and send “source code bundle “directly to me, you can send it to my e-mail “phani2431@gmail.com” .I would be really gratefull.
Download link fixed
Thanks for that tutorial!
I would be very interested with downloading that source code bundle but unfortunately it is not available anymore.
The source code for this project is very rudimentary. It might have been removed from Google Drive by mistake. If you are interested. I will try dig it out of my machine and put it under the download page
download link fixed
Hallo Jessica,
please i don´t found the link to download the projekt under the Download page. could you explain me exatly,where it is.
The link (at the end of the article) works fine for me. Try again?
Hi all, the download link has been fixed.