001package conexp.fx.gui.zoom;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025
026import conexp.fx.gui.util.Colors;
027import conexp.fx.gui.util.CoordinateUtil;
028import javafx.beans.binding.StringBinding;
029import javafx.beans.property.DoubleProperty;
030import javafx.beans.property.ObjectProperty;
031import javafx.beans.property.SimpleDoubleProperty;
032import javafx.beans.property.SimpleObjectProperty;
033import javafx.event.ActionEvent;
034import javafx.event.EventHandler;
035import javafx.geometry.Orientation;
036import javafx.geometry.Pos;
037import javafx.scene.Node;
038import javafx.scene.control.Button;
039import javafx.scene.control.Label;
040import javafx.scene.control.Slider;
041import javafx.scene.image.Image;
042import javafx.scene.image.ImageViewBuilder;
043import javafx.scene.input.MouseEvent;
044import javafx.scene.layout.ColumnConstraintsBuilder;
045import javafx.scene.layout.GridPane;
046import javafx.scene.paint.Color;
047import javafx.scene.text.Text;
048import javafx.scene.text.TextAlignment;
049import javafx.stage.Popup;
050
051public class ZoomWidget extends Label {
052
053  protected final Node                  owner;
054  private boolean                       currentlyShown     = false;
055  private boolean                       hideRequested      = false;
056  private final Popup                   zoomPopup;
057  private final ZoomPopupPane           zoomPopupPane;
058
059  protected final ObjectProperty<Color> color1             = new SimpleObjectProperty<Color>(
060                                                               Colors.fromCSSColorString("#99bcfd"));
061  protected final ObjectProperty<Color> color2             = new SimpleObjectProperty<Color>(
062                                                               Colors.fromCSSColorString("#e2ecfe"));
063  protected final ObjectProperty<Color> color3             = new SimpleObjectProperty<Color>(
064                                                               Colors.fromCSSColorString("#99bcfd"));
065
066  public final DoubleProperty           zoomFactorProperty = new SimpleDoubleProperty(1);
067
068  public ZoomWidget(final Node owner, final Image image) {
069    super();
070    this.owner = owner;
071    this.setAlignment(Pos.TOP_LEFT);
072    this.setGraphic(ImageViewBuilder
073        .create()
074        .image(image)
075        .preserveRatio(true)
076        .fitWidth(32)
077        .smooth(true)
078        .cache(true)
079        .build());
080    zoomPopupPane = new ZoomPopupPane();
081    zoomPopup = new Popup();
082    zoomPopup.getContent().add(zoomPopupPane);
083    zoomFactorProperty.bind(zoomPopupPane.sizeSlider.valueProperty());
084    addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
085
086      public void handle(MouseEvent event) {
087//        if (!currentlyShown) {
088//          currentlyShown = true;
089//          zoomPopupPane.setOpacity(0);
090        zoomPopupPane.setVisible(true);
091        zoomPopup.show(
092            owner,
093            CoordinateUtil.getScreenX(ZoomWidget.this) - 40,
094            CoordinateUtil.getScreenY(ZoomWidget.this) - 20);
095//          FadeTransitionBuilder
096//              .create()
097//              .duration(Duration.millis(100))
098//              .fromValue(0)
099//              .toValue(1)
100//              .node(zoomPopupPane)
101//              .build()
102//              .play();
103//        }
104      };
105    });
106    zoomPopup.setAutoHide(true);
107//    zoomPopupPane.addEventHandler(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
108//
109//      public void handle(MouseEvent event) {
110//        hideRequested = false;
111//      };
112//    });
113//    zoomPopupPane.addEventHandler(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {
114//
115//      public void handle(MouseEvent event) {
116//        hideRequested = true;
117//        TimelineBuilder.create().keyFrames(new KeyFrame(Duration.ONE, new EventHandler<ActionEvent>() {
118//
119//          public void handle(ActionEvent event) {
120//            if (hideRequested) {
121////              FadeTransitionBuilder
122////                  .create()
123////                  .duration(Duration.millis(100))
124////                  .fromValue(1)
125////                  .toValue(0)
126////                  .node(zoomPopupPane)
127////                  .onFinished(new EventHandler<ActionEvent>() {
128////
129////                    @Override
130////                    public void handle(ActionEvent event) {
131//              zoomPopup.hide();
132////                      zoomPopupPane.setVisible(false);
133//              currentlyShown = false;
134////                    }
135////                  })
136////                  .build()
137////                  .play();
138//            }
139//          }
140//        })).delay(Duration.millis(200)).build().play();
141//      };
142//    });
143  }
144
145  protected final class ZoomPopupPane extends GridPane {
146
147    private final Button   minusButton;
148    private final Label    label;
149    private final Text     text;
150    private final Button   plusButton;
151    protected final Slider sizeSlider;
152
153    private final int      size1 = 26;
154    private final int      size2 = 42;
155
156    public ZoomPopupPane() {
157      super();
158      this.setHgap(4);
159      this.setVgap(4);
160      this.getStyleClass().add("tooltip");
161//      this.styleProperty().bind(new StringBinding() {
162//
163//        {
164//          super.bind(color2, color3);
165//        }
166//
167//        @Override
168//        protected String computeValue() {
169//          return "-fx-background-color: linear-gradient(" + Colors.toCSSColorString(color2.get()) + ","
170//              + Colors.toCSSColorString(color3.get()) + "); -fx-background-radius: 0 0 0 0;";
171//        }
172//      });
173      this.getColumnConstraints().addAll(
174          ColumnConstraintsBuilder.create().minWidth(size1).maxWidth(size1).build(),
175          ColumnConstraintsBuilder.create().minWidth(size2).maxWidth(size2).build(),
176          ColumnConstraintsBuilder.create().minWidth(size1).maxWidth(size1).build());
177      this.minusButton = new Button("-");
178      this.plusButton = new Button("+");
179      this.minusButton.setMinSize(size1, size1);
180      this.minusButton.setMaxSize(size1, size1);
181      this.plusButton.setMinSize(size1, size1);
182      this.plusButton.setMaxSize(size1, size1);
183      this.label = new Label();
184      this.text = new Text("100%");
185//      this.text.setFontSmoothingType(FontSmoothingType.GRAY);
186      this.text.setTextAlignment(TextAlignment.CENTER);
187      this.label.setGraphic(text);
188      this.label.setAlignment(Pos.CENTER);
189      this.label.setMinWidth(size2);
190      this.label.setPrefWidth(size2);
191      this.label.setMaxWidth(size2);
192      this.sizeSlider = new Slider();
193      this.minusButton.setOnAction(new EventHandler<ActionEvent>() {
194
195        @Override
196        public void handle(ActionEvent event) {
197          sizeSlider.decrement();
198        }
199      });
200      this.plusButton.setOnAction(new EventHandler<ActionEvent>() {
201
202        @Override
203        public void handle(ActionEvent event) {
204          if (sizeSlider.getValue() == 2)
205            sizeSlider.setValue(4);
206          else
207            sizeSlider.increment();
208        }
209      });
210      this.add(minusButton, 0, 0);
211      this.add(label, 1, 0);
212      this.add(plusButton, 2, 0);
213      this.sizeSlider.setOrientation(Orientation.HORIZONTAL);
214      this.sizeSlider.setMin(0.1);
215      this.sizeSlider.setMax(2);
216      this.sizeSlider.setValue(1);
217      this.sizeSlider.setBlockIncrement(0.1);
218      this.add(sizeSlider, 0, 1, 3, 1);
219      text.textProperty().bind(new StringBinding() {
220
221        {
222          super.bind(sizeSlider.valueProperty());
223        }
224
225        @Override
226        protected String computeValue() {
227          return (int) (100 * sizeSlider.valueProperty().get()) + "%";
228        }
229      });
230    }
231  }
232}