I am attempting to determine the size of a SWT view so I can layout the widgets correctly in a plugin. I am running on Eclipse Neon with Java 8.
The code I am using follows:
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
public class ClockView extends ViewPart {
/**
* The constructor.
*/
public ClockView() {
}
/**
* This is a callback that will allow us
* to create the viewer and initialize it.
*/
#Override
public void createPartControl(Composite parent) {
final RowLayout layout = new RowLayout(SWT.HORIZONTAL);
layout.center = true;
layout.justify = true;
final Point area = parent.getSize();
final ImmutableList<Integer> clockWidths = ImmutableList.<Integer>of(100, 200, 400);
layout.marginWidth = (area.x - Iterables.getLast(clockWidths, null)) / 2;
layout.marginHeight = (area.y - Iterables.getLast(clockWidths, null)) / 2;
parent.setLayout(layout);
clockWidths.forEach(width -> {
ClockWidget c = new ClockWidget(parent, SWT.NONE);
c.setLayoutData(new RowData(width, width));
});
}
/**
* Passing the focus request to the viewer's control.
*/
#Override
public void setFocus() {
}
}
My problem is that every method I tried returns 0, 0 for the size of the view. I have tried scrollable.getClientArea and Composite.getSize.
How can I determine the area available to work with?
Sizes haven't been determined when createPartControl is called.
There will be a Resize event when the size is set.
Use getClientArea() on the main Composite to get the size of the view area.
Related
Hope you can help with a bit of a strange problem I'm having.
Take a very simple RCP application, with a toolbar along the top, where the user can switch between perspectives.
One perspective contains a Browser component.
Normally, when a user switches to the HelpDocumentsView, a browser is displayed and a bunch of red icons (highlighted) appear along the top.
But when using Edge webview2 as the browser component, using the following VM augments:
-Dorg.eclipse.swt.browser.DefaultType=edge -Dorg.eclipse.swt.browser.EdgeDir=path\to\Microsoft.WebView2.FixedVersionRuntime.103.0.1264.37.x64
The red icons are not displayed:
But when the application is resized, they appear:
Somehow the choice of browser rendering engine is changing how the ToolItems are being rendered, and I don't understand why.
I'm trying to use Edge because on Windows it'll use IE, and IE is unable to render the Javascript contained on the page I'd like to render in the browser.
PerspectiveSwitcherToolbar.java:
package com.me.rcp.perspective;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import com.me.images.ImageProvider;
public class PerspectiveSwitcherToolbar implements EventHandler {
private static final String HELP_ID = "Help"; //$NON-NLS-1$
#Inject
private static MApplication app;
#Inject
private static EPartService partService;
#Inject
private static EModelService modelService;
private static ToolBar toolBar;
#PostConstruct
private static void postConstruct(final Composite parent) {
final Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new RowLayout(SWT.HORIZONTAL));
toolBar = new ToolBar(composite, SWT.FLAT | SWT.WRAP | SWT.RIGHT);
parent.pack();
perspectiveSwitch("DataDashboard"); //$NON-NLS-1$
}
private static void perspectiveSwitch(final String id) {
final Menu switcherMenu = new Menu(toolBar);
final ToolItem choosePerspective = new ToolItem(toolBar, SWT.DROP_DOWN);
choosePerspective.setImage(ImageProvider.DATA_DASHBOARD);
choosePerspective.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(final SelectionEvent e) {
final Rectangle rect = choosePerspective.getBounds();
final Point pt = switcherMenu.getParent().toDisplay(new Point(rect.x, rect.y));
switcherMenu.setLocation(pt.x, pt.y + rect.height);
switcherMenu.setVisible(true);
}
});
final MenuItem helpMenu = new MenuItem(switcherMenu, SWT.PUSH);
helpMenu.setImage(ImageProvider.HELP);
helpMenu.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(final SelectionEvent e) {
perspectiveSwitch(HELP_ID);
}
});
partService.switchPerspective((MPerspective) modelService.find(id, app));
if (id.equals(HELP_ID)) {
for (int i = 0; i < 10; i++) {
new ToolItem(toolBar, SWT.PUSH).setImage(ImageProvider.USER_GUIDE);
}
}
}
#Override
public void handleEvent(final Event event) {
}
}
HelpDocumentsView.java:
package com.me.rcp.viewpart;
import javax.annotation.PostConstruct;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.widgets.Composite;
public class HelpDocumentsView {
#PostConstruct
public void createPartControl(final Composite parent) {
new Browser(parent, SWT.NONE);
}
}
I think the problem is that the ToolItem doesn't have a text property set, only an image - and because of this one of the nested layout() calls is skipping it
When I set it to a zero-width space (e.g. item.setText("\u200B")) it renders correctly.
I am currently working on this assignment and I can not seem to get this program to run even though I don't have any errors really popping up ? I am trying to add a time stamp to the pane as well but every time I add the "ts" name for the time stamp to the Pane or Hbox's get children code it goes red.. I am not sure what exactly I'm doing wrong if anyone can point me in the right direction id greatly appreciate it...
package PCK1;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.sql.Timestamp;
import java.util.Date;
import java.text.SimpleDateFormat;
public class MainClass
{
public static void start(Stage stage)
{
// Time Stamp
Date date = new Date();
Timestamp ts=new Timestamp(date.getTime());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(formatter.format(ts));
//Create a Circle
Circle c1 = new Circle(75,100,20);
//Create a Pane
Pane p = new Pane();
p.setMinSize(100, 150);
p.setBackground(new Background(new BackgroundFill( Color.rgb(190, 220, 190), null, null)
));
p.getChildren().addAll(c1);
//Create a Button
Button btnUp = new Button("Up");
btnUp.setOnAction((ActionEvent e) -> {double y = c1.getCenterY();
y -= 20.0;
c1.setCenterY(y);
});
Button btnDown = new Button("Down");
btnDown.setOnAction((ActionEvent e) -> {double y = c1.getCenterY();
y += 20.0;
c1.setCenterY(y);
});
//Create a HBox
HBox hb = new HBox();
hb.getChildren().addAll(btnUp, btnDown, p, ts);
hb.setBackground(new Background(new BackgroundFill(Color.rgb(150,200,150),null,null)));
hb.setMinSize(100, 50);
hb.setPadding(new Insets(10,10,10,10));
Scene scene = new Scene(hb);
stage.setScene(scene);
stage.setTitle("JavaFx");
stage.setWidth(250);
stage.setHeight(250);
stage.show();
}
}
Answer for displaying a timestamp
Specifically, for the timestamp question, see the following example code:
private Label createTimestampLabel() {
LocalDateTime now = LocalDateTime.now();
String formattedTimestamp = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
return new Label(formattedTimestamp);
}
It uses the java.time APIs explained in the Oracle Date Time tutorial to get the current time from LocalDateTime and format it as a String using a standard format.
It sets the formatted timestamp string as the text of a Label node.
Now that the returned element is a Node, it can be placed in the scene graph without generating the compile error you saw in your original example.
Using the java.time APIs is preferred over the java.sql.Timestamp and java.util.Date code in your question. You are not working with SQL, so you should not be using java.sql.Timestamp. The java.time classes also have many improvements over obsolete date and time functions used in other Java packages like java.util.
Answer in context with a re-write of your example code
There were a lot of things about the provided example application that were either wrong or annoyed me.
So I re-wrote it to match a bit more closely how I would normally write such an application.
There are maybe a hundred different small decisions made in the choices for how to implement the re-write and explaining them all here would be too verbose.
Hopefully, you can compare the re-write to your original code, note some of the differences, and learn some things from it.
GraphicControlApp.java
package org.example.javafx.demo.graphiccontrol;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class GraphicControlApp extends Application {
public void start(Stage stage) {
GraphicController graphicController = new GraphicController();
Scene scene = new Scene(graphicController.getUI());
stage.setScene(scene);
stage.setTitle("JavaFX Interactive Graphic Control Demonstration");
stage.show();
}
}
GraphicController.java
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* UI creator and controller for application logic.
*
* Normally, most UI elements would be defined externally in FXML,
* however, for a simple application, we define the UI via private functions in this class.
*/
public class GraphicController {
// amount to move the circle across the surface on interaction.
private static final double MOVEMENT_DELTA = 20.0;
// default spacing between UI elements.
private static final double SPACING = 10;
// normally the styles would be configured in an external css stylesheet,
// but we place the background definitions here for a simple application.
private static final Color SURFACE_COLOR = Color.rgb(190, 220, 190);
private static final Background surfaceBackground = createBackground(SURFACE_COLOR);
private static final Color APP_BACKGROUND_COLOR = Color.rgb(150, 200, 150);
private static final Background appBackground = createBackground(APP_BACKGROUND_COLOR);
private Button up;
private Button down;
/**
* #return the complete layout for the application with event handlers attached for logic control.
*/
public Pane getUI() {
Circle circle = new Circle(75, 100, 20);
Pane surface = createSurface(circle);
HBox controls = createControls(circle);
Label timestampLabel = createTimestampLabel();
Pane layout = createLayout(surface, controls, timestampLabel);
attachKeyboardHandlers(layout);
return layout;
}
/**
* Create a label formatted with the current time in ISO standard format (e.g. '2011-12-03T10:15:30')
*
* #return label with the current timestamp.
*/
private Label createTimestampLabel() {
LocalDateTime now = LocalDateTime.now();
String formattedTimestamp = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
return new Label(formattedTimestamp);
}
/**
* Create a surface on which a circle can move.
*
* #param circle the circle which can move on the surface.
* #return the created surface.
*/
private Pane createSurface(Circle circle) {
Pane surface = new Pane();
surface.setMinSize(100, 150);
surface.setBackground(surfaceBackground);
surface.getChildren().addAll(circle);
// we must define a clip on the surface to ensure that elements
// in the surface do not render outside the surface.
Rectangle clip = new Rectangle();
clip.widthProperty().bind(surface.widthProperty());
clip.heightProperty().bind(surface.heightProperty());
surface.setClip(clip);
return surface;
}
private VBox createLayout(Pane surface, HBox controls, Label timestampLabel) {
VBox layout = new VBox(SPACING, controls, surface, timestampLabel);
layout.setBackground(appBackground);
layout.setPadding(new Insets(SPACING));
VBox.setVgrow(surface, Priority.ALWAYS);
return layout;
}
/**
* Create controls which can control the movement of a circle.
*
* #param circle the circle which can be controlled
* #return the created controls with handlers attached for circle movement control.
*/
private HBox createControls(Circle circle) {
up = new Button("Up");
up.setOnAction(e -> moveVertically(circle, -MOVEMENT_DELTA));
down = new Button("Down");
down.setOnAction(e -> moveVertically(circle, MOVEMENT_DELTA));
return new HBox(SPACING, up, down);
}
private void moveVertically(Circle circle, double delta) {
double y = circle.getCenterY();
// we only restrict movement in the up direction,
// but allow unlimited movement in the down direction
// (even if that movement would mean that the circle would extend totally
// outside the current visible boundary of the surface).
if ((y + delta) < 0) {
return;
}
circle.setCenterY(y + delta);
}
/**
* Adds standard keyboard handling logic to the UI.
*
* Handlers are attached to the relevant scene whenever
* the scene containing the UI changes.
*
* #param layout the UI which will respond to keyboard input.
*/
private void attachKeyboardHandlers(Pane layout) {
EventHandler<KeyEvent> keyEventHandler = event -> {
switch (event.getCode()) {
case UP -> { up.requestFocus(); up.fire(); }
case DOWN -> { down.requestFocus(); down.fire(); }
}
};
layout.sceneProperty().addListener((observable, oldScene, newScene) -> {
if (oldScene != null) {
oldScene.removeEventFilter(
KeyEvent.KEY_PRESSED,
keyEventHandler
);
}
if (newScene != null) {
newScene.addEventFilter(
KeyEvent.KEY_PRESSED,
keyEventHandler
);
}
});
}
private static Background createBackground(Color surfaceColor) {
return new Background(new BackgroundFill(surfaceColor, null, null));
}
}
You should show the timestamp as text with the TextField (Doc) :
TextField myText = new TextField();
myText.setText("Time: " + formatter.format(ts));
// set what you want to the TextField object: padding, size, color etc...
p.getChildren().addAll(myText);
I have not been able to find any good questions regarding my predicament. I am trying to count/check how many times a specific item is clicked via an actionListener but am unsure of how to use an actionListener/EventHandler to document the click. This is being done in JavaFX.Pardon the potentially noobish question.
correctUrl = getCorrectUrl(); //Correct image for set
wrongUrl = getWrongUrl();
initialPos(); //everything is placed into default.
imageOne.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
imageOne.setOnMouseClicked((MouseEvent e) -> {
imageOne = flipToCard(imageOne, 1);
});
returnCardOne();
System.out.println("Tile pressed ");
event.consume();
});
The ultimate goal of the code is to notice two images have been clicked and then check to see if their URL == each-other.
If the URLs are equal, the Images should be equal. So why not compare Images? This little app demos what you are trying to achieve. Comments in the code.
Main:
import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author Sedrick
*/
public class JavaFXApplication51 extends Application {
#Override
public void start(Stage primaryStage) {
List<ImageViewTile> imageViews = new ArrayList();
List<ImageViewTile> cardsBeingMatched = new ArrayList();//Used to keep up with the current two cards that are pressed inorder to deteremine if they are a match
Image initialImage = new Image(getClass().getResourceAsStream("cardBack_blue1.png"));
Image ace = new Image(getClass().getResourceAsStream("cardSpadesA.png"));
Image king = new Image(getClass().getResourceAsStream("cardSpadesK.png"));
imageViews.add(new ImageViewTile(initialImage, ace));
imageViews.add(new ImageViewTile(initialImage, king));
imageViews.add(new ImageViewTile(initialImage, ace));
imageViews.add(new ImageViewTile(initialImage, king));
for(ImageViewTile view : imageViews)
{
//Set ImageViews' onMouseClicked handler
view.setOnMouseClicked(event->{
if(!view.isShowing())//If card face value is not showing
{
view.showFrontImage();//show it.
cardsBeingMatched.add(view);//Add card being clicked to list so it can be compared against the next card
if(cardsBeingMatched.size() == 2)//Once two cards are in the list, see if they are equal.
{
if(cardsBeingMatched.get(0).getImage().equals(cardsBeingMatched.get(1).getImage()))//If cards are equal a match is found
{
System.out.println("Match");
cardsBeingMatched.clear();//clear the list to prepare to compare the next two cards that are clicked
}
else//If cards are not equal
{
System.out.println("No match");
//wait half a second and flip cards back over
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(.5), (event1) -> {
System.out.println(".5 seconds need to pass before another mouse click!");
}));
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event1->{
cardsBeingMatched.get(0).showBackImage();
cardsBeingMatched.get(1).showBackImage();
cardsBeingMatched.clear();//clear the list to prepare to compare the next two cards that are clicked
});
}
}
}
});
}
GridPane root = new GridPane();
for(int i = 0; i < imageViews.size(); i++)
{
root.add(imageViews.get(i), i % 2, i / 2);//Add ImageViews to the GridPane
}
Scene scene = new Scene(root, 280, 380);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
ImageViewTitle Class:
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
/**
*
* #author Sedrick
*/
public final class ImageViewTile extends ImageView{
private boolean showing;
private final Image backImage;
private final Image frontImage;
public ImageViewTile(Image backImage, Image frontImage) {
this.backImage = backImage;
this.frontImage = frontImage;
this.setImage(backImage);
showing = false;
}
public void showBackImage()
{
this.setImage(backImage);
showing = false;
}
public void showFrontImage()
{
this.setImage(frontImage);
showing = true;
}
public boolean isShowing()
{
return showing;
}
}
I created this class to help me keep up with the ImageView's different states.
This question already has answers here:
A javaFX Stage could be both StageStyle.UTILITY and StageStyle.TRANSPARENT?
(5 answers)
Closed 5 years ago.
I know that you can set a stage to have a utility style "Stage.InitStyle(StageStyle.UTILITY);" and you can set it to have a transparent style "Stage.InitStyle(StageStyle.TRANSPARENT);" but can you have both in the same stage? I am tiring to make it so that the stage does not show as a window down in the start menu and I would like the stage to be invisible so that you can only see the scene.
Did find a simple way to do it:
Create a utility window and make it completely transparent so you cannot see or interact with it. Then create the wanted window and init the owner to the utility window - this will cause it to not appear in the task bar.
You can always do it the old way using Swing where that feature was available. And Swing allows you to embed JavaFX. Of course it would be preferred to have a clean mechanism without Swing, but afaik it doesn't exist (yet).
Example:
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.geom.GeneralPath;
public class Widget extends JFrame {
class DragContext {
double x;
double y;
}
public Widget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
setSize(200, 200);
toBack();
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame shape (a star)
double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } };
GeneralPath star = new GeneralPath();
star.moveTo(points[0][0], points[0][1]);
for (int k = 1; k < points.length; k++)
star.lineTo(points[k][0], points[k][2]);
star.closePath();
setShape(star);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
Widget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
#Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Widget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// Widget.this.toBack();
// Widget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
rootPane.setBackground(Background.EMPTY);
// add some node
Label label = new Label("Bright & Shiny");
label.setTextFill(Color.RED);
rootPane.getChildren().add(label);
// create scene
Scene scene = new Scene(rootPane);
// gradient fill
RadialGradient radialGradient = new RadialGradient( 270, 0.8, 0.5, 0.5, 0.7, true, CycleMethod.NO_CYCLE, new Stop( .5f, Color.YELLOW), new Stop( .7f, Color.ORANGE), new Stop( .9f, Color.ORANGERED));
scene.setFill(radialGradient);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
// set context menu for scene
scene.setOnMousePressed(mouseEvent -> {
if (mouseEvent.isSecondaryButtonDown()) {
contextMenu.show(rootPane, mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
});
// allow the frame to be dragged around
final DragContext dragDelta = new DragContext();
rootPane.setOnMousePressed(mouseEvent -> {
dragDelta.x = Widget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = Widget.this.getLocation().getY() - mouseEvent.getScreenY();
});
rootPane.setOnMouseDragged(mouseEvent -> Widget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
return scene;
}
public static void main(String[] args) {
new Widget();
}
}
Here's a screenshot of the widget that's not showing up in the task bar. Drag it with the left mouse button. Right mouse button offers a context menu with a close button.
The code above uses the swing frame's shape. The code below uses the javafx control's shape.
Here's a version in which only the control is visible. I use a reflecting label control.
If you want to send the control directly to the desktop, you need to activate the toBack() invocation. You can zoom the control with the mouse wheel. The maximum zoom size is limited to the size of the jframe.
All you need to do for your custom control is implement the code in createFxControl()
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.Reflection;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class LabelWidget extends JFrame {
class DragContext {
double x;
double y;
}
public LabelWidget() {
// decoration
setType(Type.UTILITY);
setUndecorated(true);
// make frame transparent, we only want the control to be visible
setBackground(new java.awt.Color(0,0,0,0));
setSize(400, 400);
// position
// setLocation(100, 100);
setLocationRelativeTo(null); // centers on screen
// frame operations
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// embed fx into swing
JFXPanel fxPanel = new JFXPanel();
LabelWidget.this.getContentPane().add(fxPanel);
Platform.runLater(new Runnable() {
#Override
public void run() {
// set scene in JFXPanel
fxPanel.setScene( createFxScene());
// show frame
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
LabelWidget.this.setVisible(true);
// send it to the desktop, behind all other existing windows
// ClockWidget.this.toBack();
// ClockWidget.this.repaint();
}
});
}
});
}
private Scene createFxScene() {
StackPane rootPane = new StackPane();
// make pane transparent, we only want the control to be visible
rootPane.setBackground(Background.EMPTY);
// add control
Control control = createFxControl();
rootPane.getChildren().add( control);
// create scene
Scene scene = new Scene(rootPane);
// make scene transparent, we only want the control to be visible
scene.setFill( Color.TRANSPARENT);
// context menu with close button
ContextMenu contextMenu = new ContextMenu();
MenuItem closeMenuItem = new MenuItem("Close");
closeMenuItem.setOnAction(actionEvent -> System.exit(0));
contextMenu.getItems().add(closeMenuItem);
control.setContextMenu(contextMenu);
// allow the frame to be dragged around
makeDraggable( control);
// allow zooming
makeZoomable( control);
return scene;
}
/**
* Create the JavaFX control of which we use the shape.
* #return
*/
private Control createFxControl() {
Label label = new Label( "I'm a Label");
label.setFont(new Font("Tahoma", 24));
label.setEffect(new Reflection());
return label;
}
/**
* Allow dragging of the stage / control on the desktop
* #param control
* #param stage
*/
public void makeDraggable( Control control) {
final DragContext dragDelta = new DragContext();
control.setOnMousePressed(mouseEvent -> {
dragDelta.x = LabelWidget.this.getLocation().getX() - mouseEvent.getScreenX();
dragDelta.y = LabelWidget.this.getLocation().getY() - mouseEvent.getScreenY();
});
control.setOnMouseDragged(mouseEvent -> LabelWidget.this.setLocation((int) (mouseEvent.getScreenX() + dragDelta.x), (int) (mouseEvent.getScreenY() + dragDelta.y)));
}
/**
* Allow zooming
* #param control
*/
public void makeZoomable( Control control) {
// note: in order to make it larger, we'd have to resize the stage/frame => we limit the size to 1.0 for now and allow only making the control smaller
final double MAX_SCALE = 1.0;
final double MIN_SCALE = 0.1;
control.addEventFilter(ScrollEvent.ANY, new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double delta = 1.2;
double scale = control.getScaleX();
if (event.getDeltaY() < 0) {
scale /= delta;
} else {
scale *= delta;
}
scale = clamp(scale, MIN_SCALE, MAX_SCALE);
control.setScaleX(scale);
control.setScaleY(scale);
event.consume();
}
});
}
/**
* Limit bounds of value
* #param value
* #param min
* #param max
* #return
*/
public static double clamp( double value, double min, double max) {
if( Double.compare(value, min) < 0)
return min;
if( Double.compare(value, max) > 0)
return max;
return value;
}
public static void main(String[] args) {
new LabelWidget();
}
}
Or if you have the awesome Enzo library from https://github.com/HanSolo/Enzo at hand, you can use this code:
private Control createFxControl() {
// create a clock using the enzo library from https://github.com/HanSolo/Enzo
Clock clock = ClockBuilder.create()
// .prefSize(400, 400)
.design(Clock.Design.DB)
.running(true)
.text("Berlin")
.autoNightMode(true)
.build();
return clock;
}
to create this:
A bug has been filed and fixed (super quickly) in SWT:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=305294
Just to preface this, my goal here is to print the two images into a canvas so that I can animate the canvas sliding across the screen (think iPhone), sliding the controls themselves was too CPU intensive, so this was a good alternative until I tested it on Win7. I'm open to anything that will help me solve my original problem, it doesn't have to be fixing the problem below.
Does anyone know how to get "Control.print(GC)" to work with Windows 7 Aero? I have code that works just fine in Windows XP and in Windows 7, when Aero is disabled, but the command:
control.print(GC) causes a non-top control to be effectively erased from the screen.
GC gc = new GC(image);
try {
// As soon as this code is called, calling "layout" on the controls
// causes them to disappear.
control.print(gc);
} finally {
gc.dispose();
}
I have stacked controls and would like to print the images from the current and next controls such that I can "slide" them off the screen. However, upon printing the non-top control, it is never redrawn again.
Here is some example code. (Interesting code bits are at the top and it will require pointing at SWT in order to work.)
Thanks for any and all help. As a work around, I'm thinking about swapping controls between prints to see if that helps, but I'd rather not.
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class SWTImagePrintTest {
private Composite stack;
private StackLayout layout;
private Label lblFlip;
private Label lblFlop;
private boolean flip = true;
private Button buttonFlop;
private Button buttonPrint;
/**
* Prints the control into an image
*
* #param control
*/
protected void print(Control control) {
Image image = new Image(control.getDisplay(), control.getBounds());
GC gc = new GC(image);
try {
// As soon as this code is called, calling "layout" on the controls
// causes them to disappear.
control.print(gc);
} finally {
gc.dispose();
}
}
/**
* Swaps the controls in the stack
*/
private void flipFlop() {
if (flip) {
flip = false;
layout.topControl = lblFlop;
buttonFlop.setText("flop");
stack.layout();
} else {
flip = true;
layout.topControl = lblFlip;
buttonFlop.setText("flip");
stack.layout();
}
}
private void createContents(Shell shell) {
shell.setLayout(new GridLayout(2, true));
stack = new Composite(shell, SWT.NONE);
GridData gdStack = new GridData(GridData.FILL_BOTH);
gdStack.horizontalSpan = 2;
stack.setLayoutData(gdStack);
layout = new StackLayout();
stack.setLayout(layout);
lblFlip = new Label(stack, SWT.BOLD);
lblFlip.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_CYAN));
lblFlip.setText("FlIp");
lblFlop = new Label(stack, SWT.NONE);
lblFlop.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_BLUE));
lblFlop.setText("fLoP");
layout.topControl = lblFlip;
stack.layout();
buttonFlop = new Button(shell, SWT.FLAT);
buttonFlop.setText("Flip");
GridData gdFlip = new GridData();
gdFlip.horizontalAlignment = SWT.RIGHT;
buttonFlop.setLayoutData(gdFlip);
buttonFlop.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
flipFlop();
}
});
buttonPrint = new Button(shell, SWT.FLAT);
buttonPrint.setText("Print");
GridData gdPrint = new GridData();
gdPrint.horizontalAlignment = SWT.LEFT;
buttonPrint.setLayoutData(gdPrint);
buttonPrint.addSelectionListener(new SelectionAdapter() {
#Override
public void widgetSelected(SelectionEvent e) {
print(lblFlip);
print(lblFlop);
}
});
}
/**
* #param args
*/
public static void main(String[] args) {
Shell shell = new Shell();
shell.setText("Slider Test");
shell.setSize(new Point(800, 600));
shell.setLayout(new GridLayout());
SWTImagePrintTest tt = new SWTImagePrintTest();
tt.createContents(shell);
shell.open();
Display display = Display.getDefault();
while (shell.isDisposed() == false) {
if (display.readAndDispatch() == false) {
display.sleep();
}
}
display.dispose();
}
}
This was the result of a bug:
https://bugs.eclipse.org/bugs/show_bug.cgi?id=305294
It's since been fixed. Hopefully a new packaged version of SWT will come out soon so we can officially use it.