Merge pull request #3211 from stejbac/fix-info-pane-flicker

Prevent tooltip popover flicker upon mouseover
This commit is contained in:
Christoph Atteneder 2019-11-12 12:05:01 +01:00 committed by GitHub
commit 3d5ba5b380
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 113 additions and 102 deletions

View file

@ -17,7 +17,6 @@
package bisq.desktop.components;
import bisq.common.UserThread;
import bisq.desktop.components.controlsfx.control.PopOver;
import de.jensd.fx.fontawesome.AwesomeDude;
@ -28,14 +27,10 @@ import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.layout.HBox;
import java.util.concurrent.TimeUnit;
public class AutoTooltipTableColumn<S, T> extends TableColumn<S, T> {
private Label helpIcon;
private Boolean hidePopover;
private PopOver infoPopover;
private PopOverWrapper popoverWrapper = new PopOverWrapper();
public AutoTooltipTableColumn(String text) {
super();
@ -53,46 +48,36 @@ public class AutoTooltipTableColumn<S, T> extends TableColumn<S, T> {
}
public void setTitleWithHelpText(String title, String help) {
final AutoTooltipLabel label = new AutoTooltipLabel(title);
helpIcon = new Label();
AwesomeDude.setIcon(helpIcon, AwesomeIcon.QUESTION_SIGN, "1em");
helpIcon.setOpacity(0.4);
helpIcon.setOnMouseEntered(e -> {
hidePopover = false;
final Label helpLabel = new Label(help);
helpLabel.setMaxWidth(300);
helpLabel.setWrapText(true);
showInfoPopOver(helpLabel);
});
helpIcon.setOnMouseExited(e -> {
if (infoPopover != null)
infoPopover.hide();
hidePopover = true;
UserThread.runAfter(() -> {
if (hidePopover) {
infoPopover.hide();
hidePopover = false;
}
}, 250, TimeUnit.MILLISECONDS);
});
helpIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createInfoPopOver(help)));
helpIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver());
final AutoTooltipLabel label = new AutoTooltipLabel(title);
final HBox hBox = new HBox(label, helpIcon);
hBox.setStyle("-fx-alignment: center-left");
hBox.setSpacing(4);
setGraphic(hBox);
}
private void showInfoPopOver(Node node) {
private PopOver createInfoPopOver(String help) {
Label helpLabel = new Label(help);
helpLabel.setMaxWidth(300);
helpLabel.setWrapText(true);
return createInfoPopOver(helpLabel);
}
private PopOver createInfoPopOver(Node node) {
node.getStyleClass().add("default-text");
infoPopover = new PopOver(node);
PopOver infoPopover = new PopOver(node);
if (helpIcon.getScene() != null) {
infoPopover.setDetachable(false);
infoPopover.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER);
infoPopover.show(helpIcon, -10);
}
return infoPopover;
}
}

View file

@ -19,8 +19,6 @@ package bisq.desktop.components;
import bisq.desktop.components.controlsfx.control.PopOver;
import bisq.common.UserThread;
import de.jensd.fx.fontawesome.AwesomeIcon;
import de.jensd.fx.glyphs.GlyphIcons;
@ -30,16 +28,13 @@ import javafx.scene.control.Label;
import javafx.geometry.Insets;
import java.util.concurrent.TimeUnit;
import static bisq.desktop.util.FormBuilder.getIcon;
public class InfoAutoTooltipLabel extends AutoTooltipLabel {
public static final int DEFAULT_WIDTH = 300;
private Node textIcon;
private Boolean hidePopover;
private PopOver infoPopover;
private PopOverWrapper popoverWrapper = new PopOverWrapper();
private ContentDisplay contentDisplay;
public InfoAutoTooltipLabel(String text, GlyphIcons icon, ContentDisplay contentDisplay, String info) {
@ -82,42 +77,31 @@ public class InfoAutoTooltipLabel extends AutoTooltipLabel {
private void positionAndActivateIcon(ContentDisplay contentDisplay, String info, double width) {
textIcon.setOpacity(0.4);
textIcon.getStyleClass().add("tooltip-icon");
textIcon.setOnMouseEntered(e -> {
hidePopover = false;
final Label helpLabel = new Label(info);
helpLabel.setMaxWidth(width);
helpLabel.setWrapText(true);
helpLabel.setPadding(new Insets(10));
showInfoPopOver(helpLabel);
});
textIcon.setOnMouseExited(e -> {
if (infoPopover != null)
infoPopover.hide();
hidePopover = true;
UserThread.runAfter(() -> {
if (hidePopover) {
infoPopover.hide();
hidePopover = false;
}
}, 250, TimeUnit.MILLISECONDS);
});
textIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createInfoPopOver(info, width)));
textIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver());
setGraphic(textIcon);
setContentDisplay(contentDisplay);
}
private PopOver createInfoPopOver(String info, double width) {
Label helpLabel = new Label(info);
helpLabel.setMaxWidth(width);
helpLabel.setWrapText(true);
helpLabel.setPadding(new Insets(10));
return createInfoPopOver(helpLabel);
}
private void showInfoPopOver(Node node) {
private PopOver createInfoPopOver(Node node) {
node.getStyleClass().add("default-text");
infoPopover = new PopOver(node);
PopOver infoPopover = new PopOver(node);
if (textIcon.getScene() != null) {
infoPopover.setDetachable(false);
infoPopover.setArrowLocation(PopOver.ArrowLocation.LEFT_CENTER);
infoPopover.show(textIcon, -10);
}
return infoPopover;
}
}

View file

@ -17,7 +17,6 @@
package bisq.desktop.components;
import bisq.common.UserThread;
import bisq.desktop.components.controlsfx.control.PopOver;
import de.jensd.fx.fontawesome.AwesomeIcon;
@ -29,8 +28,6 @@ import javafx.scene.layout.AnchorPane;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.util.concurrent.TimeUnit;
import lombok.Getter;
import static bisq.desktop.util.FormBuilder.getIcon;
@ -49,8 +46,7 @@ public class InfoInputTextField extends AnchorPane {
private final Label privacyIcon;
private Label currentIcon;
private PopOver popover;
private boolean hidePopover;
private PopOverWrapper popoverWrapper = new PopOverWrapper();
public InfoInputTextField() {
this(0);
@ -161,28 +157,15 @@ public class InfoInputTextField extends AnchorPane {
currentIcon.setVisible(true);
// As we don't use binding here we need to recreate it on mouse over to reflect the current state
currentIcon.setOnMouseEntered(e -> {
hidePopover = false;
showPopOver(node);
});
currentIcon.setOnMouseExited(e -> {
if (popover != null)
popover.hide();
hidePopover = true;
UserThread.runAfter(() -> {
if (hidePopover) {
popover.hide();
hidePopover = false;
}
}, 250, TimeUnit.MILLISECONDS);
});
currentIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createPopOver(node)));
currentIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver());
}
}
private void showPopOver(Node node) {
private PopOver createPopOver(Node node) {
node.getStyleClass().add("default-text");
popover = new PopOver(node);
PopOver popover = new PopOver(node);
if (currentIcon.getScene() != null) {
popover.setDetachable(false);
popover.setArrowLocation(PopOver.ArrowLocation.LEFT_TOP);
@ -190,5 +173,6 @@ public class InfoInputTextField extends AnchorPane {
popover.show(currentIcon, -17);
}
return popover;
}
}

View file

@ -34,8 +34,6 @@ import javafx.scene.text.Text;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -53,13 +51,12 @@ public class InfoTextField extends AnchorPane {
private final StringProperty text = new SimpleStringProperty();
protected final Label infoIcon;
private Label currentIcon;
private Boolean hidePopover;
private PopOver popover;
private PopOverWrapper popoverWrapper = new PopOverWrapper();
private PopOver.ArrowLocation arrowLocation;
public InfoTextField() {
arrowLocation = PopOver.ArrowLocation.RIGHT_TOP;;
arrowLocation = PopOver.ArrowLocation.RIGHT_TOP;
textField = new BisqTextField();
textField.setLabelFloat(true);
textField.setEditable(false);
@ -124,27 +121,14 @@ public class InfoTextField extends AnchorPane {
currentIcon.setVisible(true);
// As we don't use binding here we need to recreate it on mouse over to reflect the current state
currentIcon.setOnMouseEntered(e -> {
hidePopover = false;
showPopOver(node);
});
currentIcon.setOnMouseExited(e -> {
if (popover != null)
popover.hide();
hidePopover = true;
UserThread.runAfter(() -> {
if (hidePopover) {
popover.hide();
hidePopover = false;
}
}, 250, TimeUnit.MILLISECONDS);
});
currentIcon.setOnMouseEntered(e -> popoverWrapper.showPopOver(() -> createPopOver(node)));
currentIcon.setOnMouseExited(e -> popoverWrapper.hidePopOver());
}
private void showPopOver(Node node) {
private PopOver createPopOver(Node node) {
node.getStyleClass().add("default-text");
popover = new PopOver(node);
PopOver popover = new PopOver(node);
if (currentIcon.getScene() != null) {
popover.setDetachable(false);
popover.setArrowLocation(arrowLocation);
@ -152,6 +136,7 @@ public class InfoTextField extends AnchorPane {
popover.show(currentIcon, -17);
}
return popover;
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -0,0 +1,73 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.desktop.components;
import bisq.desktop.components.controlsfx.control.PopOver;
import bisq.common.UserThread;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
public class PopOverWrapper {
private PopOver popover;
private Supplier<PopOver> popoverSupplier;
private boolean hidePopover;
private PopOverState state = PopOverState.HIDDEN;
enum PopOverState {
HIDDEN, SHOWING, SHOWN, HIDING
}
public void showPopOver(Supplier<PopOver> popoverSupplier) {
this.popoverSupplier = popoverSupplier;
hidePopover = false;
if (state == PopOverState.HIDDEN) {
state = PopOverState.SHOWING;
popover = popoverSupplier.get();
UserThread.runAfter(() -> {
state = PopOverState.SHOWN;
if (hidePopover) {
// For some reason, this can result in a brief flicker when invoked
// from a 'runAfter' callback, rather than directly. So make the delay
// very short (25ms) so that we don't reach here often:
hidePopOver();
}
}, 25, TimeUnit.MILLISECONDS);
}
}
public void hidePopOver() {
hidePopover = true;
if (state == PopOverState.SHOWN) {
state = PopOverState.HIDING;
popover.hide();
UserThread.runAfter(() -> {
state = PopOverState.HIDDEN;
if (!hidePopover) {
showPopOver(popoverSupplier);
}
}, 250, TimeUnit.MILLISECONDS);
}
}
}