From b434d92334ec3cb3e9532a2a82519001a7ba8124 Mon Sep 17 00:00:00 2001 From: mindolph Date: Sat, 25 May 2024 00:53:55 +0800 Subject: [PATCH] fix: the gen-ai reframe panel shows out of the viewport if generated topics make the selected topic out of the viewport. --- .../com/mindolph/base/util/LayoutUtils.java | 2 +- .../mindolph/base/util/LayoutUtilsTest.java | 40 ++++++++++++++++ .../mindolph/mindmap/ExtraMindMapView.java | 47 +++++++++++-------- 3 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 code/mindolph-base/src/test/java/com/mindolph/base/util/LayoutUtilsTest.java diff --git a/code/mindolph-base/src/main/java/com/mindolph/base/util/LayoutUtils.java b/code/mindolph-base/src/main/java/com/mindolph/base/util/LayoutUtils.java index 6f70b21f..4661d563 100644 --- a/code/mindolph-base/src/main/java/com/mindolph/base/util/LayoutUtils.java +++ b/code/mindolph-base/src/main/java/com/mindolph/base/util/LayoutUtils.java @@ -26,7 +26,7 @@ public static Point2D bestLocation(Bounds parentBounds, Bounds hoverBounds, double padHeight = extraPadding == null ? 1f : extraPadding.getHeight(); // use 1 to avoid calculation bias double offsetX = (parentBounds.getMaxX() - padWidth) - hoverBounds.getMaxX(); double offsetY = (parentBounds.getMaxY() - padHeight) - hoverBounds.getMaxY(); - // different strategy from x and y + // different strategy from x to y double newX = offsetX > 0 ? hoverBounds.getMinX() : hoverBounds.getMinX() + offsetX; double newY = offsetY > 0 ? hoverBounds.getMinY() : hoverBounds.getMinY() - hoverBounds.getHeight() - targetDim.getHeight() - padHeight; newX = Math.max(0, newX); diff --git a/code/mindolph-base/src/test/java/com/mindolph/base/util/LayoutUtilsTest.java b/code/mindolph-base/src/test/java/com/mindolph/base/util/LayoutUtilsTest.java new file mode 100644 index 00000000..56398da7 --- /dev/null +++ b/code/mindolph-base/src/test/java/com/mindolph/base/util/LayoutUtilsTest.java @@ -0,0 +1,40 @@ +package com.mindolph.base.util; + +import com.mindolph.mfx.util.PointUtils; +import javafx.geometry.BoundingBox; +import javafx.geometry.Bounds; +import javafx.geometry.Dimension2D; +import javafx.geometry.Point2D; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * @since 1.7.6 + */ +public class LayoutUtilsTest { + + static Bounds baseParentBounds; + static Bounds baseHoverBounds; + + @BeforeAll + public static void setup() { + baseParentBounds = new BoundingBox(0, 0, 1000, 1000); + baseHoverBounds = new BoundingBox(0, 0, 1000, 1000); + } + + @Test + public void bestLocation() { + + Dimension2D dim = new Dimension2D(100, 100); + + Bounds hb = new BoundingBox(0, 0, 100, 100); + Point2D point2D = LayoutUtils.bestLocation(baseParentBounds, hb, dim, null); + System.out.println(PointUtils.pointInStr(point2D)); + + hb = new BoundingBox(950, 950, 100, 100); + point2D = LayoutUtils.bestLocation(baseParentBounds, hb, dim, null); + System.out.println(PointUtils.pointInStr(point2D)); + + + } +} diff --git a/code/mindolph-mindmap/src/main/java/com/mindolph/mindmap/ExtraMindMapView.java b/code/mindolph-mindmap/src/main/java/com/mindolph/mindmap/ExtraMindMapView.java index e96f91cd..4a464d33 100644 --- a/code/mindolph-mindmap/src/main/java/com/mindolph/mindmap/ExtraMindMapView.java +++ b/code/mindolph-mindmap/src/main/java/com/mindolph/mindmap/ExtraMindMapView.java @@ -254,6 +254,7 @@ public ContextMenu createContextMenu(MindMap model, boolean isFullScr if (opt.isPresent()) { Generator generator = opt.get(); // generator.setParentSkin(parentSkin); + // the parent panel is the editor itself for mind map. generator.setParentPane(super.getParentPane()); MenuItem menuItem = generator.contextMenuItem(null); ctxMenu.getItems().add(menuItem); @@ -261,36 +262,42 @@ public ContextMenu createContextMenu(MindMap model, boolean isFullScr generator.showInputPanel(topicUnderMouse != null ? topicUnderMouse.getText() : ""); }); generator.setOnPanelShowing(pane -> { - // do calculation on layout bounds changes to mark sure that the bounds is calculated. + // do calculation on layout bounds changes to make sure that the bounds is calculated. pane.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> { if (oldValue.equals(newValue) || newValue.getWidth() == 0 || newValue.getHeight() == 0) { return; } - pane.setVisible(false); // avoid flashing super.setDisable(true); Bounds panelBounds = pane.getBoundsInParent(); // retrieve the element from topic because the original one might have been changed. BaseElement element = (BaseElement) super.getFirstSelectedTopic().getPayload(); - Bounds boundsInViewPort = getMindMapViewSkin().getBoundsInViewport(element); - log.debug("bounds of topic in viewport: %s".formatted(BoundsUtils.boundsInString(boundsInViewPort))); - log.debug("bounds of pane: %s".formatted(BoundsUtils.boundsInString(panelBounds))); - log.debug("dimension of pane: %sx%s".formatted(pane.getWidth(), pane.getHeight())); - Bounds hoverBounds = BoundsUtils.fromPoint(new Point2D(boundsInViewPort.getMinX(), boundsInViewPort.getMaxY()), - pane.getWidth(), pane.getHeight()); - - Bounds parentBounds = super.getParentPane().getBoundsInParent(); - log.debug("target bounds: " + BoundsUtils.boundsInString(parentBounds)); - log.debug("hover bounds: " + BoundsUtils.boundsInString(hoverBounds)); - Point2D newPoint = LayoutUtils.bestLocation(parentBounds, hoverBounds, GeometryConvertUtils.boundsToDimension2D(boundsInViewPort), - new Dimension2D(config.getTheme().getSelectLineWidth(), config.getTheme().getSelectLineWidth())); - log.debug("relocated to new point: %s".formatted(PointUtils.pointInStr(newPoint))); - - Platform.runLater(() -> { - pane.relocate(newPoint.getX(), newPoint.getY() + config.getTheme().getSelectLineWidth()); - pane.setVisible(true); - pane.requestFocus(); + // the selected topic might be out of viewport once generated topics are appended to it, + // so before locating panel, the scrolling should be done first. + scrollDoneEvents.subscribeFor(1, unused -> { + pane.setVisible(false); // avoid flashing + Bounds boundsInViewPort = getMindMapViewSkin().getBoundsInViewport(element); + log.debug("bounds of topic in viewport: %s".formatted(BoundsUtils.boundsInString(boundsInViewPort))); + log.debug("bounds of pane: %s".formatted(BoundsUtils.boundsInString(panelBounds))); + log.debug("dimension of pane: %sx%s".formatted(pane.getWidth(), pane.getHeight())); + + Bounds parentBounds = getParentPane().getBoundsInParent(); + Bounds hoverBounds = BoundsUtils.fromPoint(new Point2D(boundsInViewPort.getMinX(), boundsInViewPort.getMaxY()), + pane.getWidth(), pane.getHeight()); + log.debug("parent bounds: %s".formatted(BoundsUtils.boundsInString(parentBounds))); + log.debug("hover bounds: %s".formatted(BoundsUtils.boundsInString(hoverBounds))); + Point2D newPoint = LayoutUtils.bestLocation(parentBounds, hoverBounds, GeometryConvertUtils.boundsToDimension2D(boundsInViewPort), + new Dimension2D(config.getTheme().getSelectLineWidth(), config.getTheme().getSelectLineWidth())); + log.debug("relocated to new point: %s".formatted(PointUtils.pointInStr(newPoint))); + + Platform.runLater(() -> { + pane.relocate(newPoint.getX(), newPoint.getY() + config.getTheme().getSelectLineWidth()); + pane.setVisible(true); + pane.requestFocus(); + }); }); + ensureVisibilityOfTopic(getFirstSelectedTopic()); + }); }); generator.setOnGenerated(output -> {