Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,22 @@
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;

import org.csstudio.display.builder.model.*;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Point2D;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import org.csstudio.display.builder.model.DisplayModel;
import org.csstudio.display.builder.model.UntypedWidgetPropertyListener;
import org.csstudio.display.builder.model.Widget;
import org.csstudio.display.builder.model.WidgetPropertyListener;
import org.csstudio.display.builder.model.properties.PredefinedColorMaps;
import org.csstudio.display.builder.model.properties.WidgetColor;
import org.csstudio.display.builder.representation.ToolkitRepresentation;
Expand All @@ -47,18 +61,11 @@
import javafx.collections.ObservableList;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ChoiceDialog;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
Expand Down Expand Up @@ -452,47 +459,139 @@ else if (level_spec.equalsIgnoreCase(Messages.Zoom_Height))
* @param zoom Zoom level: 1.0 for 100%, 0.5 for 50%, ZOOM_ALL, ZOOM_WIDTH, ZOOM_HEIGHT
* @return Zoom level actually used
*/
private double setZoom(double zoom)
private double setZoom(final double zoom)
{
final double zoomToSet;
if (zoom <= 0.0)
{ // Determine zoom to fit outline of display into available space
final Bounds available = model_root.getLayoutBounds();
final Bounds outline = widget_pane.getLayoutBounds();

// 'outline' will wrap the actual widgets when the display
// is larger than the available viewport.
// So it can be used to zoom 'out'.
// But when the viewport is much larger than the widget,
// the JavaFX outline grows to fill the viewport,
// so falling back to the self-declared model width and height
// to zoom 'in'.
// This requires displays to be created with
// correct width/height properties.
final double zoom_x, zoom_y;
if (outline.getWidth() > available.getWidth())
zoom_x = available.getWidth() / outline.getWidth();
else if (model.propWidth().getValue() > 0)
zoom_x = available.getWidth() / model.propWidth().getValue();
else
zoom_x = 1.0;

if (outline.getHeight() > available.getHeight())
zoom_y = available.getHeight() / outline.getHeight();
else if (model.propHeight().getValue() > 0)
zoom_y = available.getHeight() / model.propHeight().getValue();
else
zoom_y = 1.0;

if (zoom == ZOOM_WIDTH)
zoom = zoom_x;
else if (zoom == ZOOM_HEIGHT)
zoom = zoom_y;
else // Assume ZOOM_ALL
zoom = Math.min(zoom_x, zoom_y);
{ // Determine zoom to fit outline of display into available space.
// In order to determine the actual bounds within which an OPI
// can be displayed, model_root.getViewportBounds() is called
// and then the width/height of the scrollbars are added/subtracted
// in order to determine the bounds both _with_ and _without_
// scrollbars being displayed.
boolean hScrollbarVisible = false;
boolean vScrollbarVisible = false;
double vScrollbarWidth = 0.0;
double hHcrollbarHeight = 0.0;
{
List<ScrollBar> scrollbars = model_root.lookupAll(".scroll-bar").stream().filter(node -> node instanceof ScrollBar).map(node -> (ScrollBar) node).collect(Collectors.toUnmodifiableList());
List<ScrollBar> modelRootScrollbars = scrollbars.stream().filter(scrollbar -> scrollbar.getParent() == model_root).collect(Collectors.toUnmodifiableList());
for (ScrollBar scrollBar : modelRootScrollbars) {
if (scrollBar.getOrientation() == Orientation.HORIZONTAL) {
hScrollbarVisible = scrollBar.isVisible();
hHcrollbarHeight = scrollBar.getLayoutBounds().getHeight();
}
else if (scrollBar.isVisible() && scrollBar.getOrientation() == Orientation.VERTICAL) {
vScrollbarVisible = scrollBar.isVisible();
vScrollbarWidth = scrollBar.getLayoutBounds().getWidth();
}
}
}

final Bounds viewportBounds = model_root.getViewportBounds();
final BoundingBox layoutBoundsWithoutScrollbars = new BoundingBox(viewportBounds.getMinX(),
viewportBounds.getMinY(),
Math.max(0.0, viewportBounds.getWidth() + (vScrollbarVisible ? vScrollbarWidth : 0.0)),
Math.max(0.0, viewportBounds.getHeight() + (hScrollbarVisible ? hHcrollbarHeight : 0.0)));
final BoundingBox layoutBoundsWithScrollbars = new BoundingBox(viewportBounds.getMinX(),
viewportBounds.getMinY(),
Math.max(0.0, viewportBounds.getWidth() - (vScrollbarVisible ? 0.0 : vScrollbarWidth)),
Math.max(0.0, viewportBounds.getHeight() - (hScrollbarVisible ? 0.0 : hHcrollbarHeight)));

final double zoomXWithScrollbarsRounded;
final double zoomXWithoutScrollbarsRounded;
final double zoomYWithScrollbarsRounded;
final double zoomYWithoutScrollbarsRounded;

{
final Bounds outline = widget_pane.getLayoutBounds();
// 'outline' will wrap the actual widgets when the display
// is larger than the available viewport.
// So it can be used to zoom 'out'.
// But when the viewport is much larger than the widget,
// the JavaFX outline grows to fill the viewport,
// so falling back to the self-declared model width and height
// to zoom 'in'.
// This requires displays to be created with
// correct width/height properties.
final double zoomXWithoutScrollbars, zoomYWithoutScrollbars;
final double zoomXWithScrollbars, zoomYWithScrollbars;
if (outline.getWidth() > layoutBoundsWithScrollbars.getWidth()) {
zoomXWithoutScrollbars = layoutBoundsWithoutScrollbars.getWidth() / outline.getWidth();
zoomXWithScrollbars = layoutBoundsWithScrollbars.getWidth() / outline.getWidth();
}
else if (model.propWidth().getValue() > 0) {
zoomXWithoutScrollbars = layoutBoundsWithoutScrollbars.getWidth() / model.propWidth().getValue();
zoomXWithScrollbars = layoutBoundsWithScrollbars.getWidth() / model.propWidth().getValue();
}
else {
zoomXWithoutScrollbars = 1.0;
zoomXWithScrollbars = 1.0;
}

if (outline.getHeight() > layoutBoundsWithScrollbars.getHeight()) {
zoomYWithoutScrollbars = layoutBoundsWithoutScrollbars.getHeight() / outline.getHeight();
zoomYWithScrollbars = layoutBoundsWithScrollbars.getHeight() / outline.getHeight();
}
else if (model.propHeight().getValue() > 0) {
zoomYWithoutScrollbars =layoutBoundsWithoutScrollbars.getHeight() / model.propHeight().getValue();
zoomYWithScrollbars = layoutBoundsWithScrollbars.getHeight() / model.propHeight().getValue();
}
else {
zoomYWithoutScrollbars = 1.0;
zoomYWithScrollbars = 1.0;
}

// Round down the zoom-level in order to avoid situations where the
// zoom-level is instead being rounded up, causing a scrollbar to
// be displayed when a scrollbar is not needed.
zoomXWithScrollbarsRounded = Math.floor(zoomXWithScrollbars * 1000.0) / 1000.0;
zoomXWithoutScrollbarsRounded = Math.floor(zoomXWithoutScrollbars * 1000.0) / 1000.0;
zoomYWithScrollbarsRounded = Math.floor(zoomYWithScrollbars * 1000.0) / 1000.0;
zoomYWithoutScrollbarsRounded = Math.floor(zoomYWithoutScrollbars * 1000.0) / 1000.0;
}

if (zoom == ZOOM_WIDTH) {
if (zoomXWithoutScrollbarsRounded * model.propHeight().getValue() > layoutBoundsWithoutScrollbars.getHeight()) {
// Setting zoomToSet to 'zoomXWithoutScrollbarsRounded'
// would result in the horizontal scrollbar being shown.
// Therefore, set zoomToSet = zoomXWithScrollbarsRounded
zoomToSet = zoomXWithScrollbarsRounded;
}
else {
zoomToSet = zoomXWithoutScrollbarsRounded;
}
}
else if (zoom == ZOOM_HEIGHT) {
if (zoomYWithoutScrollbarsRounded * model.propWidth().getValue() > layoutBoundsWithoutScrollbars.getWidth()) {
// Setting zoomToSet to 'zoomYWithoutScrollbarsRounded'
// would result in the vertical scrollbar being shown.
// Therefore, set zoomToSet = zoomYWithScrollbarsRounded:
zoomToSet = zoomYWithScrollbarsRounded;
}
else {
zoomToSet = zoomYWithoutScrollbarsRounded;
}
}
else {
// Assume ZOOM_ALL
if (zoomYWithoutScrollbarsRounded * model.propWidth().getValue() > layoutBoundsWithoutScrollbars.getWidth() ||
zoomXWithoutScrollbarsRounded * model.propHeight().getValue() > layoutBoundsWithoutScrollbars.getHeight()) {
// Setting zoomToSet to 'Math.min(zoomXWithoutScrollbarsRounded, zoomYWithScrollbarsRounded)'
// would result in at least either the vertical or the horizontal scrollbar being shown.
// Therefore, set zoomToSet = Math.min(zoomXWithoutScrollbarsRounded, zoomYWithoutScrollbarsRounded):
zoomToSet = Math.min(zoomXWithScrollbarsRounded, zoomYWithScrollbarsRounded); // Assume ZOOM_ALL
}
else {
zoomToSet = Math.min(zoomXWithoutScrollbarsRounded, zoomYWithoutScrollbarsRounded);
}
}
}
else {
zoomToSet = zoom;
}

widget_parent.getTransforms().setAll(new Scale(zoom, zoom));
widget_pane.getTransforms().setAll(new Scale(zoom, zoom));
widget_pane.getTransforms().setAll(new Scale(zoomToSet, zoomToSet));
// Appears similar to using this API:
// widget_parent.setScaleX(zoom);
// widget_parent.setScaleY(zoom);
Expand All @@ -506,7 +605,7 @@ else if (zoom == ZOOM_HEIGHT)
if (isEditMode())
updateModelSizeIndicators();

return zoom;
return zoomToSet;
}

/** @return Zoom factor, 1.0 for 1:1 */
Expand Down