I am new to JavaFx and i am making a simple drawing program where i draw shapes. The problem i am having now is that i dont know how to make the circle appear on the screen when i click on the screen. So first I want to press a button that says "Circle" and then when i click on the canvas i want it to spawn. (I am switching between scenebuilder and intellij btw).
This is some of my program:
Classes:
public abstract class Shape {
double x;
double y;
double width;
double height;
public Color color = Color.WHITE;
public void creatingShapes(double x, double y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void setColor(Color color) {
this.color = color;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Color getColor() {
return color;
}
public abstract void draw(GraphicsContext g);
}
public class Circle extends Shape {
#Override
public void draw(GraphicsContext g) {
g.setFill(color);
g.fillOval(200,200,200,200);
g.fillRect(getX(),getY(),getWidth(),getHeight());
g.setStroke(Color.BLACK);
g.strokeOval(getX(),getY(),getWidth(),getHeight());
====================================================
Controller class:
public class HelloController {
#FXML
private Button logoutButton;
#FXML
private BorderPane scenePane;
Stage stage;
#FXML
private ColorPicker myColorPicker;
#FXML
private ChoiceBox<String> myChoiceBox;
#FXML
private Button circle;
#FXML
private GraphicsContext g;
private final Shape[] shapes = new Shape[500];
Canvas canvas = new Canvas(310,333);
boolean drawShape = true;
int count = 0;
private final Color currentColor = Color.BLUE;
private final String[] menuAlternatives = {"Sparning", "Testing?", "Exit?"};
public void onCircleClicked() {
circle.setOnAction((event) -> addShape(new Circle()));
}
//skapa shapes
public void addShape(Shape shape) {
shape.setColor(currentColor);
shape.creatingShapes(10,10,150,100);
shapes[count] = shape;
count++;
paintCanvas();
}
public void changeColor(ActionEvent event) {
Color myColor = myColorPicker.getValue();
scenePane.setBackground(new Background(new BackgroundFill(myColor, null, null)));
}
public void initialize() {
g = canvas.getGraphicsContext2D();
}
public void paintCanvas() {
g.setFill(Color.WHITE);
g.fillRect(0,0,400,400);
Arrays.stream(shapes, 0, count).forEach(s -> s.draw(g));
}
Placing a circle on canvas with mouse event
From Doc
public void fillOval(double x,
double y,
double w,
double h)
Fills an oval using the current fill paint.
This method will be affected by any of the global common or fill attributes
as specified in the Rendering Attributes Table.
Parameters:
x - the X coordinate of the upper left bound of the oval.
y - the Y coordinate of the upper left bound of the oval.
w - the width at the center of the oval.
h - the height at the center of the oval.
As we can see x and y params from fillOval() will put the circle at the left upper corner of its bound . to spawn at its very center , we need to subtract half widht and half height to x and y coordinates given by mouseclick event.
this is a single class javafx app you can try
App.java
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
Canvas canvas = new Canvas(640, 480);
GraphicsContext context = canvas.getGraphicsContext2D();
canvas.setOnMouseClicked(e -> {
double x = e.getX();
double y = e.getY();
context.setFill(Color.AQUAMARINE);
context.setStroke(Color.BLACK);
context.fillOval(x - 20, y - 20, 40, 40);
context.strokeOval(x - 20, y - 20, 40, 40);
});
var scene = new Scene(new Group(canvas), 640, 480);
stage.setScene(scene);
stage.setTitle("spawn with mouse click");
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I have a problem again with the JavaFX Chart : D
Context :
I had Popup/Label on my chart to display the value on hover : JavaFX LineChart Hover Values (Jewelsea answer)
Problem :
But when the point are near the edges of chart, the popup is hidden by them.
The chart with problem, I highlighted the edges of chart.
This is a problem, because my popup is bigger and display more informations (x value, y value and the data serie)
Possible solutions :
May I can check where the edges are, and if the popup is hide. In this case, I should shift the popup. But when I look doc, I didn't found the right method :
XYChart
XYChart.Data#nodeProperty
May I can put the popup above the chart. Like z-index in CSS.
The code :
import javafx.application.Application;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(int... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
data.setNode(
new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i]
)
);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Thank you in advance ! And my apologies about my english, still learning !
I have been looking on the JavaFX CSS reference guide and I could'nt find anything to simply solve your problem.
A possible solution is to translate your symbol depending on how near it is to the max or min value.
I wrote something like this, based on your code :
/**
* Displays a LineChart which displays the value of a plotted Node when you hover over the Node.
* #author original, jewelsea https://gist.github.com/jewelsea
*/
public class LineChartWithHover extends Application {
#SuppressWarnings("unchecked")
#Override public void start(Stage stage) {
final LineChart lineChart = new LineChart(
new NumberAxis(), new NumberAxis(),
FXCollections.observableArrayList(
new XYChart.Series(
"My portfolio",
FXCollections.observableArrayList(
plot(0, 14, 15, 24, 34, 36, 22, 55, 43, 17, 29, 25)
)
)
)
);
lineChart.setCursor(Cursor.CROSSHAIR);
lineChart.setTitle("Stock Monitoring, 2013");
stage.setScene(new Scene(lineChart, 500, 400));
stage.show();
System.out.println("test 1 = "+lineChart.getProperties());
}
/** #return plotted y values for monotonically increasing integer x values, starting from x=1 */
public ObservableList<XYChart.Data<Integer, Integer>> plot(Integer... y) {
final ObservableList<XYChart.Data<Integer, Integer>> dataset = FXCollections.observableArrayList();
int i = 0;
List<Integer> list = Arrays.asList(y);
int min = Collections.min(list);
int max = Collections.max(list);
int minThreshold = 5;
int maxThreshold = 5;
while (i < y.length) {
final XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
int topMargin = 0;
if(y[i] <= min + minThreshold) {
topMargin = -50;
} else if (y[i] >= max - maxThreshold) {
topMargin = 50;
}
StackPane stackPane = new HoveredThresholdNode(
(i == 0) ? 0 : y[i-1],
y[i],
topMargin
);
data.setNode(stackPane);
dataset.add(data);
i++;
}
return dataset;
}
/** a node which displays a value on hover, but is otherwise empty */
class HoveredThresholdNode extends StackPane {
HoveredThresholdNode(int priorValue, int value, int topMargin) {
setPrefSize(15, 15);
final Label label = createDataThresholdLabel(priorValue, value);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
setCursor(Cursor.NONE);
toFront();
setMargin(label, new Insets(topMargin,0,0,0));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
getChildren().clear();
setCursor(Cursor.CROSSHAIR);
}
});
}
private Label createDataThresholdLabel(int priorValue, int value) {
final Label label = new Label(value + "");
label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");
if (priorValue == 0) {
label.setTextFill(Color.DARKGRAY);
} else if (value > priorValue) {
label.setTextFill(Color.FORESTGREEN);
} else {
label.setTextFill(Color.FIREBRICK);
}
label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
return label;
}
}
public static void main(String[] args) { launch(args); }
}
Basically, I am just saying that all values <= min+5 and >= max-5 must be translated.
The +/- 5 is arbitrary and should be calculated from the ticks gap and plot scale to have a perfect repositioning. Anyway, without performing any maths, it is still quite satisfying.
Based on Mr Kwoinkwoin solution, I wrote my own.
Im not sure if its possible to optimize it or improve it. But seems to be working for me so far.
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.NumberAxis.DefaultFormatter;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import static javafx.scene.layout.StackPane.setMargin;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class StockLineChartApp extends Application {
private LineChart<Number, Number> chart;
private Series<Number, Number> series;
private NumberAxis xAxis;
private ZonedDateTime time;
public StockLineChartApp() {
time = ZonedDateTime.of(LocalDateTime.of(LocalDate.of(2000, Month.JANUARY, 1), LocalTime.NOON), ZoneId.systemDefault());
}
public Parent createContent() {
xAxis = new NumberAxis();
xAxis.setLabel("Date/Time");
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelFormatter(new StringConverter<Number>() {
#Override
public String toString(Number t) {
long longValue = t.longValue();
ZonedDateTime zd = convertLongToZonedDateTime(longValue);
String formatDate = formatDate(zd, "dd/MM/yyyy");
return formatDate;
}
#Override
public Number fromString(String string) {
ZonedDateTime dl = ZonedDateTime.parse(string, DateTimeFormatter.ofPattern("dd/MM/yyyy"));
long toEpochMilli = dl.toEpochSecond();
//DateTimeFormatter.ofPattern(string).p
return toEpochMilli;
}
});
final NumberAxis yAxis = new NumberAxis();
yAxis.setAutoRanging(true);
chart = new LineChart<>(xAxis, yAxis);
chart.setCursor(Cursor.CROSSHAIR);
chart.setAlternativeRowFillVisible(true);
chart.setAlternativeColumnFillVisible(true);
// setup chart
//final String stockLineChartCss= getClass().getResource("StockLineChart.css").toExternalForm();
//chart.getStylesheets().add(stockLineChartCss);
chart.setCreateSymbols(true);
chart.setAnimated(true);
chart.setLegendVisible(true);
chart.setTitle("ACME Company Stock");
yAxis.setLabel("Share Price");
yAxis.setTickLabelFormatter(new DefaultFormatter(yAxis, "$", null));
// add starting data
series = new Series<>();
series.setName("Data por Peça");
for (double m = 0; m < (10); m++) {
long data = nextTime();
addData(data, (long) (Math.random() * 10));
System.out.println(data);
}
//chart.
chart.getData().add(series);
//chart.getData().add(hourDataSeries);
return chart;
}
private void addData(long x, long y) {
Data<Number, Number> data = new Data<Number, Number>(x, y);
series.getData().add(data);
ZonedDateTime zd = convertLongToZonedDateTime(x);
String formatDate = formatDate(zd, "dd/MM/yyyy");
//String text = "(" + formatDate + ";" + y + ")";
String text = y + "";
if (text.length() > 4) {
text = text.substring(0, 4);
}
String t = formatDate + "\nValor: " + text;
data.setNode(new HoveredThresholdNode(t, data));
}
public static long convertZonedDateTimeToLong(ZonedDateTime zonedDateTime) {
long e = zonedDateTime.toInstant().toEpochMilli();
return e;
}
private long nextTime() {
time = time.plusYears(10);
return convertZonedDateTimeToLong(time);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent createContent = createContent();
//
final StackPane pane = new StackPane();
pane.getChildren().add(createContent);
final Scene scene = new Scene(pane, 500, 400);
//new ZoomManager(pane, chart, series);
//
primaryStage.setScene(scene);
primaryStage.show();
}
public static String formatDate(ZonedDateTime ts, String format) {
try {
if (ts == null) {
return "";
}
String format1 = ts.format(DateTimeFormatter.ofPattern(format));
return format1;
} catch (Exception ex) {
ex.printStackTrace();
}
return "";
}
public static ZonedDateTime convertLongToZonedDateTime(long e) {
Instant i = Instant.ofEpochMilli(e);
ZonedDateTime ofInstant = ZonedDateTime.ofInstant(i, ZoneId.systemDefault());
return ofInstant;
}
/**
* Java main for when running without JavaFX launcher
*/
public static void main(String[] args) {
launch(args);
}
public class HoveredThresholdNode extends StackPane {
//Reference
private Data<Number, Number> data;
private Label label;
private String value;
public HoveredThresholdNode(String value, Data<Number, Number> data) {
this.data = data;
this.value = value;
this.label = new Label(value);
this.label.getStyleClass().clear();
this.getStyleClass().clear();
this.label.setStyle("-fx-font-size: 12; fx-text-fill: black;");
this.label.getStyleClass().addAll("default-color0", "chart-line-symbol", "chart-series-line");
this.label.setWrapText(true);
this.label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().setAll(label);
toFront();
boolean close_top = false, close_right = false, close_bottom = false, close_left = false;
long min_x = Long.MAX_VALUE;
long max_x = Long.MIN_VALUE;
long min_y = Long.MAX_VALUE;
long max_y = Long.MIN_VALUE;
ObservableList<Series<Number, Number>> chartSeries = chart.getData();
for (Series<Number, Number> s : chartSeries) {
ObservableList<Data<Number, Number>> chartData = s.getData();
for (Data<Number, Number> d : chartData) {
Number xValue = d.getXValue();
Number yValue = d.getYValue();
long kx = xValue.longValue();
long ky = yValue.longValue();
if (kx < min_x) {
min_x = kx;
}
if (kx > max_x) {
max_x = kx;
}
if (ky < min_y) {
min_y = ky;
}
if (ky > max_y) {
max_y = ky;
}
}
}
if (data.getXValue().longValue() - max_x == 0) {
close_right = true;
}
if (data.getXValue().longValue() - min_x == 0) {
close_left = true;
}
if (data.getYValue().longValue() - min_y == 0) {
close_bottom = true;
}
if (data.getYValue().longValue() - max_y == 0) {
close_top = true;
}
// System.out.println("\n");
// System.out.println(" close_right " + close_right);
// System.out.println(" close_left " + close_left);
// System.out.println(" close_bottom " + close_bottom);
// System.out.println(" close_top " + close_top);
double top = 0;
double right = 0;
double bottom = 0;
double left = 0;
if (close_top) {
top = 50;
}
if (close_bottom) {
bottom = 50;
}
if (close_right) {
right = 50;
}
if (close_left) {
left = 50;
}
setMargin(label, new Insets(top, right, bottom, left));
}
});
setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
getChildren().clear();
}
});
}
public HoveredThresholdNode copy() {
HoveredThresholdNode copy = new HoveredThresholdNode(value, data);
return copy;
}
}
}
I'm trying to implement a scene with a ScrollPane in which the user can drag a node around and scale it dynamically. I have the dragging and scaling with the mouse wheel working as well as a reset zoom, but I'm having trouble with the calculations to fit the node to the width of the parent.
Here is my code as an sscce.
(works) Mouse wheel will zoom in and out around the mouse pointer
(works) Left or right mouse press to drag the rectangle around
(works) Left double-click to reset the zoom
(doesn't work) Right double-click to fit the width
If I zoom in or out or change the window size, the fit to width does not work.
If anyone can help me with the calculations to fit the node to the width of the parent, I would very much appreciate it.
EDITED:
I marked the method that is not working correctly. It is fitWidth(), which is invoked by right mouse button double-clicking.
I edited the text of the question for clarity and focus
Hopefully this is more clear now.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ZoomAndPanExample extends Application {
private ScrollPane scrollPane = new ScrollPane();
private final DoubleProperty zoomProperty = new SimpleDoubleProperty(1.0d);
private final DoubleProperty deltaY = new SimpleDoubleProperty(0.0d);
private final Group group = new Group();
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
scrollPane.setPannable(true);
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
scrollPane.setVbarPolicy(ScrollBarPolicy.NEVER);
AnchorPane.setTopAnchor(scrollPane, 10.0d);
AnchorPane.setRightAnchor(scrollPane, 10.0d);
AnchorPane.setBottomAnchor(scrollPane, 10.0d);
AnchorPane.setLeftAnchor(scrollPane, 10.0d);
AnchorPane root = new AnchorPane();
Rectangle rect = new Rectangle(80, 60);
rect.setStroke(Color.NAVY);
rect.setFill(Color.NAVY);
rect.setStrokeType(StrokeType.INSIDE);
group.getChildren().add(rect);
// create canvas
PanAndZoomPane panAndZoomPane = new PanAndZoomPane();
zoomProperty.bind(panAndZoomPane.myScale);
deltaY.bind(panAndZoomPane.deltaY);
panAndZoomPane.getChildren().add(group);
SceneGestures sceneGestures = new SceneGestures(panAndZoomPane);
scrollPane.setContent(panAndZoomPane);
panAndZoomPane.toBack();
scrollPane.addEventFilter( MouseEvent.MOUSE_CLICKED, sceneGestures.getOnMouseClickedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_PRESSED, sceneGestures.getOnMousePressedEventHandler());
scrollPane.addEventFilter( MouseEvent.MOUSE_DRAGGED, sceneGestures.getOnMouseDraggedEventHandler());
scrollPane.addEventFilter( ScrollEvent.ANY, sceneGestures.getOnScrollEventHandler());
root.getChildren().add(scrollPane);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
class PanAndZoomPane extends Pane {
public static final double DEFAULT_DELTA = 1.3d;
DoubleProperty myScale = new SimpleDoubleProperty(1.0);
public DoubleProperty deltaY = new SimpleDoubleProperty(0.0);
private Timeline timeline;
public PanAndZoomPane() {
this.timeline = new Timeline(60);
// add scale transform
scaleXProperty().bind(myScale);
scaleYProperty().bind(myScale);
}
public double getScale() {
return myScale.get();
}
public void setScale( double scale) {
myScale.set(scale);
}
public void setPivot( double x, double y, double scale) {
// note: pivot value must be untransformed, i. e. without scaling
// timeline that scales and moves the node
timeline.getKeyFrames().clear();
timeline.getKeyFrames().addAll(
new KeyFrame(Duration.millis(200), new KeyValue(translateXProperty(), getTranslateX() - x)),
new KeyFrame(Duration.millis(200), new KeyValue(translateYProperty(), getTranslateY() - y)),
new KeyFrame(Duration.millis(200), new KeyValue(myScale, scale))
);
timeline.play();
}
/**
* !!!! The problem is in this method !!!!
*
* The calculations are incorrect, and result in unpredictable behavior
*
*/
public void fitWidth () {
double scale = getParent().getLayoutBounds().getMaxX()/getLayoutBounds().getMaxX();
double oldScale = getScale();
double f = (scale / oldScale)-1;
double dx = getTranslateX() - getBoundsInParent().getMinX() - getBoundsInParent().getWidth()/2;
double dy = getTranslateY() - getBoundsInParent().getMinY() - getBoundsInParent().getHeight()/2;
double newX = f*dx + getBoundsInParent().getMinX();
double newY = f*dy + getBoundsInParent().getMinY();
setPivot(newX, newY, scale);
}
public void resetZoom () {
double scale = 1.0d;
double x = getTranslateX();
double y = getTranslateY();
setPivot(x, y, scale);
}
public double getDeltaY() {
return deltaY.get();
}
public void setDeltaY( double dY) {
deltaY.set(dY);
}
}
/**
* Mouse drag context used for scene and nodes.
*/
class DragContext {
double mouseAnchorX;
double mouseAnchorY;
double translateAnchorX;
double translateAnchorY;
}
/**
* Listeners for making the scene's canvas draggable and zoomable
*/
public class SceneGestures {
private DragContext sceneDragContext = new DragContext();
PanAndZoomPane panAndZoomPane;
public SceneGestures( PanAndZoomPane canvas) {
this.panAndZoomPane = canvas;
}
public EventHandler<MouseEvent> getOnMouseClickedEventHandler() {
return onMouseClickedEventHandler;
}
public EventHandler<MouseEvent> getOnMousePressedEventHandler() {
return onMousePressedEventHandler;
}
public EventHandler<MouseEvent> getOnMouseDraggedEventHandler() {
return onMouseDraggedEventHandler;
}
public EventHandler<ScrollEvent> getOnScrollEventHandler() {
return onScrollEventHandler;
}
private EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
sceneDragContext.mouseAnchorX = event.getX();
sceneDragContext.mouseAnchorY = event.getY();
sceneDragContext.translateAnchorX = panAndZoomPane.getTranslateX();
sceneDragContext.translateAnchorY = panAndZoomPane.getTranslateY();
}
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
panAndZoomPane.setTranslateX(sceneDragContext.translateAnchorX + event.getX() - sceneDragContext.mouseAnchorX);
panAndZoomPane.setTranslateY(sceneDragContext.translateAnchorY + event.getY() - sceneDragContext.mouseAnchorY);
event.consume();
}
};
/**
* Mouse wheel handler: zoom to pivot point
*/
private EventHandler<ScrollEvent> onScrollEventHandler = new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double delta = PanAndZoomPane.DEFAULT_DELTA;
double scale = panAndZoomPane.getScale(); // currently we only use Y, same value is used for X
double oldScale = scale;
panAndZoomPane.setDeltaY(event.getDeltaY());
if (panAndZoomPane.deltaY.get() < 0) {
scale /= delta;
} else {
scale *= delta;
}
double f = (scale / oldScale)-1;
double dx = (event.getX() - (panAndZoomPane.getBoundsInParent().getWidth()/2 + panAndZoomPane.getBoundsInParent().getMinX()));
double dy = (event.getY() - (panAndZoomPane.getBoundsInParent().getHeight()/2 + panAndZoomPane.getBoundsInParent().getMinY()));
panAndZoomPane.setPivot(f*dx, f*dy, scale);
event.consume();
}
};
/**
* Mouse click handler
*/
private EventHandler<MouseEvent> onMouseClickedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton().equals(MouseButton.PRIMARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.resetZoom();
}
}
if (event.getButton().equals(MouseButton.SECONDARY)) {
if (event.getClickCount() == 2) {
panAndZoomPane.fitWidth();
}
}
}
};
}
}
I found the answer. I was looking at the wrong calculations, assuming it to be related to the translations. The real culprit was the calculation for the difference in scale. I simply changed this:
double f = (scale / oldScale)-1;
to this:
double f = scale - oldScale;
I try to create a class of object (picturebox), then calling it my main code to create other picturebox from a menu of different picturebox. I want to fix them on a panel to draw shcema or organigrams.
thanks
class _createpicture
{
public int p;
public int p_2;
PictureBox pictureBox2 = new PictureBox();
Panel panel1 = new Panel();
Color color = new Color();
Pen pen = new Pen(Color.Black, 1);
Bitmap flag = new Bitmap(40, 30);
ContextMenu cm = new ContextMenu();
public int x;
public int y;
public _createpicture () {
pictureBox2.Size = new Size(40, 30);
pictureBox2.MouseClick += new MouseEventHandler(pic_mouseclick);
pictureBox2.MouseMove += new MouseEventHandler(pic_mouseMouve);
MenuItem item_1 = cm.MenuItems.Add("Définir");
MenuItem item_2 = cm.MenuItems.Add("Supprimer");
item_1.Click += new EventHandler(item_1_Click);
item_2.Click += new EventHandler(item_2_Click);
}
private void item_1_Click(object sender, EventArgs e)
{
var myForm = new Form2();
myForm.Show();
}
private void item_2_Click(object sender, EventArgs e)
{
pictureBox2.Dispose();
}
public void _create_picture(PictureBox pictureBox2, int p, int p_2, Color color)
{
}
public void _add(Panel panel1, int p, int p_2, Color color)
{
panel1.Controls.Add(pictureBox2);
pictureBox2.BackColor = color;
Graphics flagGraphics = Graphics.FromImage(flag);
flagGraphics.DrawRectangle(pen, p, p_2, 20, 10);
flagGraphics.FillRectangle(Brushes.Red, p, p_2, 40, 30);
pictureBox2.Image = flag;
panel1.Controls.Add(pictureBox2);
}
public void pic_mouseclick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
pictureBox2.ContextMenu = cm;
}
if (e.Button == MouseButtons.Left)
{
x = e.X;
y = e.Y;
}
}
public void pic_mouseMouve(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
pictureBox2.Left += (e.X - x);
pictureBox2.Top += (e.Y - y);
}
}}
public partial class Form1 : Form
{
_createpicture _createpicture10 = new _createpicture();
.
.
.
.
if (radioButton2.Checked) {
_createpicture10._create_picture(picturebox10, e.X, e.Y, Color.Red); _createpicture10._add(this.panel1, e.X, e.Y, Color.Red);
}
}
Subj. I found this and translated into code below. But it shows only a dark-blue rectangle with no transparency at all.
class MainClass
{
public static void Main (string[] args)
{
Application.Init();
Gtk.Window w = new Gtk.Window(Gtk.WindowType.Toplevel);
w.ExposeEvent += new ExposeEventHandler(w_ExposeEvent);
w.DeleteEvent += new DeleteEventHandler(w_DeleteEvent);
w.Decorated = false;
w.BorderWidth = 20;
w.AppPaintable = true;
w.Colormap = w.Screen.RgbaColormap;
w.Show();
Application.Run();
}
static void w_ExposeEvent(object o, ExposeEventArgs args)
{
Context context = CairoHelper.Create(args.Event.Window);
context.SetSourceRGBA(0.0, 0.0, 50.0, 0.2);
context.Operator = Operator.Source;
context.Paint();
}
static void w_DeleteEvent(object o, DeleteEventArgs args)
{
Application.Quit();
}
}