I am trying to create a custom control in JavaFx. The main aim is to create a node that acts as a button. It is simply a Label that would be styled with css to look like a button. I have created a class MyControl that extends the control class like so:
public class MyControl extends Control {
#Override
protected String getUserAgentStylesheet() {
return MyControl.class.getResource("myControl.css").toExternalForm();
}
public MyControl() {
getStyleClass().add("new-control");
}
public MyControl(String item) {
getStyleClass().add("new-control");
}
}
}
For the SkinBase class MyControlSkin I have:
public class MyControlSkin extends SkinBase<MyControlSkin> {
public MyControlSkin(MyControlSkin control) {
super(control);
Label label = new Label("Some text here");
getChildren.add(label);
}
}
In the myControl.css file i simply have:
.tree-node-view {
-fx-skin: "treeviewsample.TreeViewSkin";
}
.label {
-fx-text-fill: -fx-text-background-color;
}
I have created the css for this but the problem is that I don't see the label displayed on the scene:
MyControl control = new MyControl();
but no Label displays on the screen. Please help me I am new to this so ask me for more information if in case my question does not make sense.
I am using javaFx 2.2
I don't know if you made errors transferring the code from your IDE to the post, but as it stands the skin class will not compile. You need
public class MyControlSkin extends SkinBase<MyControl> {
public MyControlSkin(MyControl control) {
super(control);
Label label = new Label("Some text here");
getChildren().add(label);
}
}
The css must define an fx:skin property for a class that matches the control. So you need
.new-control {
-fx-skin: "MyControlSkin";
}
.label {
-fx-text-fill: -fx-text-background-color;
}
Related
I am using validation that puts a red border around my TextFields
nameTF.setStyle("-fx-text-box-border: red;");
And set it back to default using
nameTF.setStyle("-fx-text-box-border: #D3D3D3;");
I am trying to use a CSS:
Hoping to increase the width of the border so that the focused border is inside/or outside of my red validation border (so both show). Now when I click on a cell the selection border replaces the current border color and width...?
.text-input {
-fx-border-color: #D3D3D3;
-fx-border-width: 3 ;
}
.text-field:focused{
-fx-border-color: blue ;
-fx-border-width: 1 ;
}
Also, I have noticed that by using
.text-input {
-fx-border-color: #D3D3D3;
-fx-border-width: 3 ;
}
The red
nameTF.setStyle("-fx-text-box-border: red;");
doesn't show up at all?
What's Going on and what I am hoping for:
Thanks!
Suggested Approach
I suggest that you look at how controlsfx accomplishes this and either use that library or learn from their approach by studying their source code (if appropriate).
I checked the controlsfx validation code which applies style classes for validation feedback. The sample usage provides feedback via css drop shadow effects. This is a straightforward solution and easier than customizing backgrounds.
I tried this using a drop shadow effect to mark an invalid text field and it appeared to work well for me.
For complex controls other than text fields (like range sliders where the range tick text is also shadowed), the drop shadow approach looks a little weird, so perhaps for those, you may want to do something more sophisticated.
Example application
This sets or unsets an invalid style class on a field as appropriate.
.invalid {
-fx-effect: dropshadow(three-pass-box, tomato, 3, 0.8, 0, 0);
}
A drop shadow effect is triggered when the invalid style class is active, to provide the user with an indication that the field is invalid.
Refer to the CSS effect documentation to understand the settings for the dropshadow effect.
States
Invalid value with focus:
Invalid value without focus:
Valid value with focus:
Valid value without focus:
This sample is not meant as a robust validation framework, it is just an example to show how you might provide validation feedback styles for a text field.
The example operates based on a standard style class, but you could use a psuedo-class instead if you wanted to look into that. For example, similar to the :focus psuedo-class, you could implement the CSS :invalid psuedo-class.
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ValidatorApp extends Application {
public static final String CSS = "data:text/css," + // language=CSS
"""
.invalid {
-fx-effect: dropshadow(three-pass-box, tomato, 3, 0.8, 0, 0);
}
""";
#Override
public void start(Stage stage) throws Exception {
TextField integerTextField = new TextField();
IntegerTextFieldValidator validator = new IntegerTextFieldValidator();
validator.validate(integerTextField);
integerTextField.textProperty().addListener(o ->
validator.validate(integerTextField)
);
VBox layout = new VBox(10,
labelField("Enter an integer: ", integerTextField),
labelField("Enter any value: ", new TextField("foobar"))
);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
HBox labelField(String labelText, Node field) {
HBox box = new HBox(10, new Label(labelText), field);
box.setAlignment(Pos.BASELINE_LEFT);
return box;
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.scene.Node;
import javafx.scene.control.TextField;
public class IntegerTextFieldValidator {
private static final String INVALID_STYLECLASS = "invalid";
public void validate(TextField textField) {
String text = textField.getText();
if (text == null) {
setInvalid(textField);
return;
}
try {
Integer.parseInt(text);
setValid(textField);
} catch (NumberFormatException e) {
setInvalid(textField);
}
}
private void setInvalid(Node node) {
if (!node.getStyleClass().contains(INVALID_STYLECLASS)) {
node.getStyleClass().add(INVALID_STYLECLASS);
}
}
private void setValid(Node node) {
node.getStyleClass().removeAll(INVALID_STYLECLASS);
}
}
Currently, I'm playing around with JavaFX as I'm writing a Snake game for my Java Fundamentals class final project. I'm not that new to creating simple games with animations as I've made a bit of them using PyGame module and SDL in C. Anyway, now I'm quite struggling with understanding the correlations of some objects in JavaFX, especially when combined with SceneBuilder's FXMLs.
I just can't understand how to create an equivalent of gameloop I used to implement in PyGame or SDL. What I want to do with the code below is to enter the gameloop as soon as a new Game object is created and draw the state of the game continuously on the gameCanvas created in the SceneBuilder. I think I can easily manage all the stuff later, but I just can't sort it out how to create a legit bond between the FXML canvas and the gameloop I want to run.
GameController.java
public class GameController implements Initializable, ControlledScreen {
#FXML
private Canvas gameCanvas;
#Override
public void setScreenParent(ScreensController screenPage) {
// THIS IS FOR SCENE MANAGEMENT CONCEPT
}
Game.java
public class Game implements Runnable {
public static final int EASY = 1,
MEDIUM = 2,
HARD = 3;
int difficultyLevel, score = 0;
Snake snake;
Food food;
boolean isRunning = true;
public void setLevelEasy() {
difficultyLevel = EASY;
}
public void setLevelMedium() {
difficultyLevel = MEDIUM;
}
public void setLevelHard() {
difficultyLevel = HARD;
}
#Override
public void run() {
while (isRunning) {
}
}
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
}
You cant use FXML file to create new scene. Use this instead
public class Screen extends Application implements Runnable
{
#Override
public void start ( Stage primaryStage )
{
Pane pane = new Pane ();
Scene scene = new Scene(pane,500,300);
primaryStage.setScene ( scene );
primaryStage.show ();
}
#Override
public void run ()
{
launch ();
}
}
anything you want to add goes to pane element your canvas etc.
and by the way make dificulty enum not int like
enum {easy,medium,hard}
that way nobody can set dificulty level to something does not exist.
Its been a while since i built a desktop JAVA application.. after lots of documentation and doing implementation tests, i still have not found an image grid solution.
Either Java lacks such a ready-to-use component (?!) or you tell me to brush up my google-fu. :)
I have a very simple technical premises: a JDialog that allows the user to pick an image. Input is a Map<Integer, String> list that holds filenames. Output is the Integer key the user chose. GUI also is simple: user chooses 1 image using mouse or keyboard, and dialog closes. All images are 80x80px and loaded from filename, not a resource.
I tried several approaches so far this morning:
Search for components/widgets that show scrollable imagegrid that can flow to the left. (no dice)
Search for components/widgets that show scrollable imagegrid (no dice)
Search for any components/widgets/gui-libs (no dice .. do these even exist?!)
Try and implement myJList.setModel(), but i cant get it to just take my Map<> and show thumbnails. (overcomplicates!)
Try and implement myJPanel.setlayout(new FlowLayout(..)) with several myJPanel.add(new JButton(..)) which just creates a bunch of JButton on a JPanel, which each need a event handler. I wonder how scrolling and keyboard input is going to work out, and how i'm supposed to keep/reference my Map<> key values. (overcomplicates?)
In lieu of your answer, i am now working on the latter, which should work but i cant believe everyone needs to reinvent the same GUI wheel here. How to have the user select an image from my Map<Integer, String>? Are there JAVA libraries/widgets/components that i should look to avoid this?
I hope this isn't being modded down, i have no working implementation with error to show you guys.. this question is about how/where to find the components or what approaches would be better. Its 2014 and i cant believe that JAVA still requires me to build my own "GUI component" just to see some images.. not even Delphi or Mono does that.
If all you want is a grid of images, and having them selectable, consider using a JList, filling it with appropriate ImageIcons, and giving it a ListSelectionListener. In the Listener you can close the enclosing dialog when a selection has been made.
You state:
Try and implement myJList.setModel(), but i cant get it to just take my Map<> and show thumbnails. (overcomplicates!)
You need to use your Map to populate your ListModel, and set that Model to the JList's model.
For example:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
#SuppressWarnings("serial")
public class ImageGridPanel extends JPanel {
public static final String PATH = "http://images-2.drive.com.au/2011/";
public static final String[] CARS = {
"04/15/2308961/giulietta_1024-80x80.jpg",
"11/18/2781958/audi-a1-sportback_600-80x80.jpg",
"12/23/2856762/fiat-500-80x80.jpg",
"01/12/2129944/Honda-Civic-Sedan-concept-1_600-80x80.jpg",
"12/23/2856581/mini-roadster-80x80.jpg",
"12/23/2856571/hyundai-veloster-80x80.jpg",
"12/23/2856771/hyundai-i30-80x80.jpg",
"12/23/2856580/mini-coupe-80x80.jpg" };
private DefaultListModel<Car> carModel = new DefaultListModel<>();
final JTextField textField = new JTextField(20);
public ImageGridPanel() {
for (String carPath : CARS) {
String path = PATH + carPath;
try {
URL imgUrl = new URL(path);
BufferedImage img = ImageIO.read(imgUrl);
ImageIcon icon = new ImageIcon(img);
String name = carPath.substring(carPath.lastIndexOf("/"));
name = name.substring(1, name.lastIndexOf("-"));
carModel.addElement(new Car(name, icon));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
ShowGridAction showAction = new ShowGridAction("Car Grid", carModel);
JButton showGridBtn = new JButton(showAction);
add(showGridBtn);
add(textField);
}
private class ShowGridAction extends AbstractAction {
private CarGridPanel carGridPanel;
public ShowGridAction(String name, DefaultListModel<Car> carModel) {
super(name);
carGridPanel = new CarGridPanel(carModel);
}
public CarGridPanel getCarGridPanel() {
return carGridPanel;
}
#Override
public void actionPerformed(ActionEvent e) {
Window win = SwingUtilities.getWindowAncestor((Component) e.getSource());
JDialog dialog = new JDialog(win, "Cars", ModalityType.APPLICATION_MODAL);
dialog.add(carGridPanel);
dialog.pack();
dialog.setLocationRelativeTo(null);
int x = dialog.getLocation().x;
int y = dialog.getLocation().y - 150;
dialog.setLocation(x, y);
dialog.setVisible(true);
Car selectedCar = carGridPanel.getSelectedCar();
if (selectedCar != null) {
textField.setText(selectedCar.getName());
}
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("ImageGrid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ImageGridPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class Car {
String name;
Icon icon;
public Car(String name, Icon icon) {
this.name = name;
this.icon = icon;
}
public String getName() {
return name;
}
public Icon getIcon() {
return icon;
}
}
#SuppressWarnings("serial")
class CarGridPanel extends JPanel {
private JList<Car> carList = new JList<>();
private Car selectedCar;
public CarGridPanel(ListModel<Car> model) {
carList.setModel(model);
carList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
carList.setVisibleRowCount(2);
carList.setCellRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value != null) {
Car carValue = (Car) value;
value = carValue.getIcon();
} else {
value = "";
}
return super.getListCellRendererComponent(list, value, index,
isSelected, cellHasFocus);
}
});
setLayout(new BorderLayout());
add(new JScrollPane(carList));
carList.addListSelectionListener(new ListListener());
}
public Car getSelectedCar() {
return selectedCar;
}
private class ListListener implements ListSelectionListener {
#Override
public void valueChanged(ListSelectionEvent e) {
selectedCar = carList.getSelectedValue();
Window win = SwingUtilities.getWindowAncestor(CarGridPanel.this);
win.dispose();
}
}
}
No, Java doesn't have what you want.
Java is a general-purpose programming language, not a toolset, particularly not a specialized desktop GUI toolset. This is not a denigration of the language, just a statement of a purpose that it was not developed to fulfill.
If Delphi or Mono or anything has your particular widget, then I suggest you program in that, instead. This is not a denigration of you, just an observation that, if you do not want to put together the widget you want from lower-level components and code, then Java is not the right language/tool for you to use to do it.
As for not believing that Java "still requires" you to build your own component, I can only say that you don't get to choose which languages provide which features. I'm just as glad Java isn't littered with your component and the hundreds of others that people like you would come up with that they think Java should provide. It's big enough as it is.
I'm working on a SmartGWT project where I'd like my main navigation to be done via a treegrid. The treegrid renders proprerly and its DataSource is functioning appropriately as well. The treegrid is correctly situated to the left of the mainView Canvas.
What I can't seem to figure out is how to switch the contents of the mainView Canvas based on what is selected in the NavigationTree. I've mimicked the functionality I'd like by adding new windows to the existing Canvas, but I can't find an example demonstrating how to clear the canvas entirely and replace it with a new Window.
Am I on the right track here? Can anyone point me at an example that shows roughly what I'm trying to accomplish?
public class NavigationTree extends TreeGrid {
public NavigationTree(Canvas mainView)
{
setDataSource(NavigationDataSource.getInstance());
setAutoFetchData(true);
setShowHeader(false);
addNodeClickHandler(new NavClickHandler(mainView));
}
// Handler for clicking an item on the Navigation Tree.
private class NavClickHandler implements NodeClickHandler
{
private Canvas mainView;
public NavClickHandler(Canvas mainView)
{
super();
this.mainView = mainView;
}
#Override
public void onNodeClick(NodeClickEvent event)
{
Window window = new Window();
window.setWidth(300);
window.setHeight(230);
window.setCanDragReposition(true);
window.setCanDragResize(true);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("huzzah!"));
window.setParentElement(mainView);
window.redraw();
}
}
}
You can keep the mainView canvas, clear its children (if any is set) and then set the newly created window as its new child. Something like the following as the body of your click handler:
Window window = new Window();
window.setWidth(300);
window.setHeight(230);
window.setCanDragReposition(true);
window.setCanDragResize(true);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("huzzah!"));
for (Canvas child: mainView.getChildren()) {
mainView.removeChild(child);
}
mainView.addChild(window);
I managed to accomplish what I needed with the following change to the event handler code:
public NavClickHandler(UI ui) //UI extends HLayout
{
this.ui = ui;
}
#Override
public void onNodeClick(NodeClickEvent event) {
Window window = new Window();
window.setWidth100();
window.setHeight100();
window.setHeaderControls(HeaderControls.HEADER_LABEL);
window.setTitle(event.getNode().getAttribute("name"));
window.addItem(new Label("Huzzah!"));
ui.setMainView(window);
}
...and the following change to my main UI layout:
public void setMainView(Canvas canvas)
{
mainView.destroy();
mainView = canvas;
addMember(mainView);
this.redraw();
}
I am trying to do something pretty common with GWT - creating a button behavior with an image and a text by positioning the text on top of the image.
I have used the HTML widget but how can I make the text not selectable?
I recently had the same need for a GWT button which allows to add image AND text. So I coded one myself since the already available implementations didn't work. I wrote a post on my blog but I also copy the code here:
Here's the code for my custom button
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Image;
public class CustomButton extends Button {
private String text;
public CustomButton(){
super();
}
public void setResource(ImageResource imageResource){
Image img = new Image(imageResource);
String definedStyles = img.getElement().getAttribute("style");
img.getElement().setAttribute("style", definedStyles + "; vertical-align:middle;");
DOM.insertBefore(getElement(), img.getElement(), DOM.getFirstChild(getElement()));
}
#Override
public void setText(String text) {
this.text = text;
Element span = DOM.createElement("span");
span.setInnerText(text);
span.setAttribute("style", "padding-left:3px; vertical-align:middle;");
DOM.insertChild(getElement(), span, 0);
}
#Override
public String getText() {
return this.text;
}
}
Usage with UiBinder XML definition
...
<!-- ImageBundle definition -->
<ui:with field="res" type="com.sample.client.IDevbookImageBundle" />
...
<d:CustomButton ui:field="buttonSave" text="Save" resource="{res.save}"></d:CustomButton>
The screenshot of such a button:
Do you mean to get rid of the text select cursor, or make the text completely unselectable?
To make it look like something clickable, you can use the cursor CSS rule.
.widget_style {cursor: pointer;}
Actually making it unselectable is not well supported from what I understand. It is in the CSS3 specs with user-select.
.widget_style {user-select:none;}
I would use the Button-Widget and call the setHTML() function.
You could use this code:
public class Custombutton extends Button {
public CustomButton(String text, String img) {
this.setHTML(text + "<br><img src=\"" + img + "\">");
}
}
You just have to provide the text and the img url when you create the button.
Did you try the CustomButton class of gwt?
here is the link:
http://projectpossibility.org/projects/word_prediction/gwt-linux-1.4.60/doc/javadoc/com/google/gwt/user/client/ui/CustomButton.html
A class that allows the addition of arbitrary widgets to a Button:
import java.util.ArrayList;
import java.util.Iterator;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
public class ButtonWithWidgets extends Button implements HasWidgets
{
private ArrayList<Widget> widgets = new ArrayList<Widget>();
#Override
public void add(Widget w)
{
DOM.insertChild(getElement(), w.getElement(), widgets.size());
}
#Override
public void clear()
{
for (Widget widget : widgets)
{
DOM.removeChild(getElement(), widget.getElement());
}
widgets.clear();
}
#Override
public Iterator<Widget> iterator()
{
return widgets.iterator();
}
#Override
public boolean remove(Widget w)
{
if (widgets.indexOf(w) != -1)
{
widgets.remove(w);
DOM.removeChild(getElement(), w.getElement());
return true;
}
else
return false;
}
}
with UiBinder:
...
<customwidgets:ButtonWithWidgets>
<g:Label>Whatever</g:Label>
<g:Image url="etc.png"/>
</customwidgets:ButtonWithWidgets>