修复 JFXPopup 在 HiDPI 显示器上播放动画过程中内容 Y 轴方向缩放异常的问题 (#5064)
This commit is contained in:
171
HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java
Normal file
171
HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.jfoenix.controls;
|
||||
|
||||
import com.jfoenix.skins.JFXPopupSkin;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.DefaultProperty;
|
||||
import javafx.beans.property.ObjectProperty;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.geometry.Point2D;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.PopupControl;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.stage.Window;
|
||||
|
||||
/// JFXPopup is the material design implementation of a popup.
|
||||
///
|
||||
/// @author Shadi Shaheen
|
||||
/// @version 2.0
|
||||
/// @since 2017-03-01
|
||||
@DefaultProperty(value = "popupContent")
|
||||
public class JFXPopup extends PopupControl {
|
||||
|
||||
public enum PopupHPosition {
|
||||
RIGHT, LEFT
|
||||
}
|
||||
|
||||
public enum PopupVPosition {
|
||||
TOP, BOTTOM
|
||||
}
|
||||
|
||||
/// Creates empty popup.
|
||||
public JFXPopup() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/// creates popup with a specified container and content
|
||||
///
|
||||
/// @param content the node that will be shown in the popup
|
||||
public JFXPopup(Region content) {
|
||||
setPopupContent(content);
|
||||
initialize();
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
this.setAutoFix(false);
|
||||
this.setAutoHide(true);
|
||||
this.setHideOnEscape(true);
|
||||
this.setConsumeAutoHidingEvents(false);
|
||||
this.getStyleClass().add(DEFAULT_STYLE_CLASS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new JFXPopupSkin(this);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* Setters / Getters *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
private final ObjectProperty<Region> popupContent = new SimpleObjectProperty<>(new Pane());
|
||||
|
||||
public final ObjectProperty<Region> popupContentProperty() {
|
||||
return this.popupContent;
|
||||
}
|
||||
|
||||
public final Region getPopupContent() {
|
||||
return this.popupContentProperty().get();
|
||||
}
|
||||
|
||||
public final void setPopupContent(final Region popupContent) {
|
||||
this.popupContentProperty().set(popupContent);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* Public API *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
/// show the popup using the default position
|
||||
public void show(Node node) {
|
||||
this.show(node, PopupVPosition.TOP, PopupHPosition.LEFT, 0, 0);
|
||||
}
|
||||
|
||||
/// show the popup according to the specified position
|
||||
///
|
||||
/// @param vAlign can be TOP/BOTTOM
|
||||
/// @param hAlign can be LEFT/RIGHT
|
||||
public void show(Node node, PopupVPosition vAlign, PopupHPosition hAlign) {
|
||||
this.show(node, vAlign, hAlign, 0, 0);
|
||||
}
|
||||
|
||||
/// show the popup according to the specified position with a certain offset
|
||||
///
|
||||
/// @param vAlign can be TOP/BOTTOM
|
||||
/// @param hAlign can be LEFT/RIGHT
|
||||
/// @param initOffsetX on the x-axis
|
||||
/// @param initOffsetY on the y-axis
|
||||
public void show(Node node, PopupVPosition vAlign, PopupHPosition hAlign, double initOffsetX, double initOffsetY) {
|
||||
if (!isShowing()) {
|
||||
if (node.getScene() == null || node.getScene().getWindow() == null) {
|
||||
throw new IllegalStateException("Can not show popup. The node must be attached to a scene/window.");
|
||||
}
|
||||
Window parent = node.getScene().getWindow();
|
||||
final Point2D origin = node.localToScene(0, 0);
|
||||
final double anchorX = parent.getX() + origin.getX()
|
||||
+ node.getScene().getX() + (hAlign == PopupHPosition.RIGHT ? ((Region) node).getWidth() : 0);
|
||||
final double anchorY = parent.getY() + origin.getY()
|
||||
+ node.getScene()
|
||||
.getY() + (vAlign == PopupVPosition.BOTTOM ? ((Region) node).getHeight() : 0);
|
||||
this.show(parent, anchorX, anchorY);
|
||||
((JFXPopupSkin) getSkin()).reset(vAlign, hAlign, initOffsetX, initOffsetY);
|
||||
Platform.runLater(() -> ((JFXPopupSkin) getSkin()).animate());
|
||||
}
|
||||
}
|
||||
|
||||
public void show(Window window, double x, double y, PopupVPosition vAlign, PopupHPosition hAlign, double initOffsetX, double initOffsetY) {
|
||||
if (!isShowing()) {
|
||||
if (window == null) {
|
||||
throw new IllegalStateException("Can not show popup. The node must be attached to a scene/window.");
|
||||
}
|
||||
Window parent = window;
|
||||
final double anchorX = parent.getX() + x + initOffsetX;
|
||||
final double anchorY = parent.getY() + y + initOffsetY;
|
||||
this.show(parent, anchorX, anchorY);
|
||||
((JFXPopupSkin) getSkin()).reset(vAlign, hAlign, initOffsetX, initOffsetY);
|
||||
Platform.runLater(() -> ((JFXPopupSkin) getSkin()).animate());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
super.hide();
|
||||
((JFXPopupSkin) getSkin()).init();
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* Stylesheet Handling *
|
||||
* *
|
||||
**************************************************************************/
|
||||
|
||||
/// Initialize the style class to 'jfx-popup'.
|
||||
///
|
||||
/// This is the selector class from which CSS can be used to style
|
||||
/// this control.
|
||||
private static final String DEFAULT_STYLE_CLASS = "jfx-popup";
|
||||
}
|
||||
139
HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java
Normal file
139
HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (c) 2016 JFoenix
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.jfoenix.skins;
|
||||
|
||||
import com.jfoenix.controls.JFXPopup;
|
||||
import com.jfoenix.controls.JFXPopup.PopupHPosition;
|
||||
import com.jfoenix.controls.JFXPopup.PopupVPosition;
|
||||
import com.jfoenix.effects.JFXDepthManager;
|
||||
import javafx.animation.*;
|
||||
import javafx.animation.Animation.Status;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.scene.transform.Scale;
|
||||
import javafx.util.Duration;
|
||||
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||
import org.jackhuang.hmcl.ui.animation.Motion;
|
||||
|
||||
/// # Material Design Popup Skin
|
||||
/// TODO: REWORK
|
||||
///
|
||||
/// @author Shadi Shaheen
|
||||
/// @version 2.0
|
||||
/// @since 2017-03-01
|
||||
public class JFXPopupSkin implements Skin<JFXPopup> {
|
||||
|
||||
protected JFXPopup control;
|
||||
protected StackPane container = new StackPane();
|
||||
protected Region popupContent;
|
||||
protected Node root;
|
||||
|
||||
private Animation animation;
|
||||
protected Scale scale;
|
||||
|
||||
public JFXPopupSkin(JFXPopup control) {
|
||||
this.control = control;
|
||||
// set scale y to 0.01 instead of 0 to allow layout of the content,
|
||||
// otherwise it will cause exception in traverse engine, when focusing the 1st node
|
||||
scale = new Scale(1.0, 0.01, 0, 0);
|
||||
popupContent = control.getPopupContent();
|
||||
container.getStyleClass().add("jfx-popup-container");
|
||||
container.getChildren().add(popupContent);
|
||||
container.getTransforms().add(scale);
|
||||
container.setOpacity(0);
|
||||
root = JFXDepthManager.createMaterialNode(container, 4);
|
||||
animation = AnimationUtils.isAnimationEnabled() ? getAnimation() : null;
|
||||
}
|
||||
|
||||
public void reset(PopupVPosition vAlign, PopupHPosition hAlign, double offsetX, double offsetY) {
|
||||
// postion the popup according to its animation
|
||||
scale.setPivotX(hAlign == PopupHPosition.RIGHT ? container.getWidth() : 0);
|
||||
scale.setPivotY(vAlign == PopupVPosition.BOTTOM ? container.getHeight() : 0);
|
||||
root.setTranslateX(hAlign == PopupHPosition.RIGHT ? -container.getWidth() + offsetX : offsetX);
|
||||
root.setTranslateY(vAlign == PopupVPosition.BOTTOM ? -container.getHeight() + offsetY : offsetY);
|
||||
}
|
||||
|
||||
public final void animate() {
|
||||
if (animation != null) {
|
||||
if (animation.getStatus() == Status.STOPPED) {
|
||||
container.setOpacity(1);
|
||||
animation.playFromStart();
|
||||
}
|
||||
} else {
|
||||
container.setOpacity(1);
|
||||
popupContent.setOpacity(1);
|
||||
scale.setX(1.0);
|
||||
scale.setY(1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JFXPopup getSkinnable() {
|
||||
return control;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node getNode() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (animation != null) {
|
||||
animation.stop();
|
||||
animation = null;
|
||||
}
|
||||
container = null;
|
||||
control = null;
|
||||
popupContent = null;
|
||||
root = null;
|
||||
}
|
||||
|
||||
protected Animation getAnimation() {
|
||||
Interpolator interpolator = Motion.EASE;
|
||||
return new Timeline(
|
||||
new KeyFrame(
|
||||
Duration.ZERO,
|
||||
new KeyValue(popupContent.opacityProperty(), 0, interpolator),
|
||||
new KeyValue(scale.xProperty(), 0, interpolator),
|
||||
new KeyValue(scale.yProperty(), 0, interpolator)
|
||||
),
|
||||
new KeyFrame(Motion.SHORT4,
|
||||
new KeyValue(popupContent.opacityProperty(), 0, interpolator),
|
||||
new KeyValue(scale.xProperty(), 1, interpolator)
|
||||
),
|
||||
new KeyFrame(Motion.MEDIUM2,
|
||||
new KeyValue(popupContent.opacityProperty(), 1, interpolator),
|
||||
new KeyValue(scale.yProperty(), 1, interpolator)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
if (animation != null)
|
||||
animation.stop();
|
||||
container.setOpacity(0);
|
||||
scale.setX(1.0);
|
||||
scale.setY(0.01);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user