IDEA-270877 Tool windows dnd: add "drag distance" threshold for tool window buttons and headers

IDEA-270760 Highlighting area of a dragged tool window takes the whole editor height

GitOrigin-RevId: 67909bb5be94c305b15f3d733593c61ab985e2d2
This commit is contained in:
Vassiliy.Kudryashov
2021-06-06 15:48:39 +03:00
committed by intellij-monorepo-bot
parent a2a3048501
commit ebc002960c
6 changed files with 67 additions and 40 deletions

View File

@@ -3,6 +3,7 @@ package com.intellij.openapi.wm;
import com.intellij.ide.ui.UISettings;
import com.intellij.ui.UIBundle;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -47,7 +48,7 @@ public final class ToolWindowAnchor {
return this == TOP || this == BOTTOM;
}
public static @NotNull ToolWindowAnchor get(int swingOrientationConstant) {
public static @NotNull ToolWindowAnchor get(@MagicConstant(intValues = {SwingConstants.CENTER, SwingConstants.TOP, SwingConstants.LEFT, SwingConstants.BOTTOM, SwingConstants.RIGHT}) int swingOrientationConstant) {
switch(swingOrientationConstant) {
case SwingConstants.TOP:
return TOP;

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.util.Weighted;
import com.intellij.openapi.wm.IdeGlassPane;
import com.intellij.openapi.wm.IdeGlassPaneUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.update.Activatable;
import com.intellij.util.ui.update.UiNotifyConnector;
import org.jetbrains.annotations.NotNull;
@@ -32,8 +33,6 @@ public abstract class MouseDragHelper<T extends JComponent> extends MouseAdapter
private IdeGlassPane myGlassPane;
@NotNull
private final Disposable myParentDisposable;
private Dimension myDelta;
private boolean myDetachPostponed;
private boolean myDetachingMode;
private boolean myCancelled;
@@ -124,13 +123,6 @@ public abstract class MouseDragHelper<T extends JComponent> extends MouseAdapter
myPressPointScreen = new RelativePoint(e).getScreenPoint();
myPressedOnScreenPoint = new Point(myPressPointScreen);
processMousePressed(e);
myDelta = new Dimension();
if (myDragComponent.isShowing()) {
final Point delta = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), myDragComponent);
myDelta.width = delta.x;
myDelta.height = delta.y;
}
}
@Override
@@ -268,9 +260,7 @@ public abstract class MouseDragHelper<T extends JComponent> extends MouseAdapter
}
private boolean isWithinDeadZone(@NotNull MouseEvent e) {
final Point screen = new RelativePoint(e).getScreenPoint();
return Math.abs(myPressPointScreen.x - screen.x - myDelta.width) < DRAG_START_DEADZONE &&
Math.abs(myPressPointScreen.y - screen.y - myDelta.height) < DRAG_START_DEADZONE;
return myPressPointScreen.distance(e.getLocationOnScreen()) < JBUI.scale(DRAG_START_DEADZONE);
}
@Override

View File

@@ -10,6 +10,7 @@ import com.intellij.ui.ColorUtil;
import com.intellij.ui.Gray;
import com.intellij.ui.paint.LinePainter2D;
import com.intellij.util.ui.JBSwingUtilities;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.StartupUiUtil;
import com.intellij.util.ui.UIUtil;
import org.intellij.lang.annotations.MagicConstant;
@@ -32,6 +33,7 @@ class Stripe extends JPanel implements UISettingsListener {
private static final Dimension EMPTY_SIZE = new Dimension();
static final Key<Rectangle> VIRTUAL_BOUNDS = Key.create("Virtual stripe bounds");
@MagicConstant(intValues = {SwingConstants.CENTER, SwingConstants.TOP, SwingConstants.LEFT, SwingConstants.BOTTOM, SwingConstants.RIGHT})
private final int anchor;
private final List<StripeButton> buttons = new ArrayList<>();
@@ -41,9 +43,9 @@ class Stripe extends JPanel implements UISettingsListener {
private JComponent myDragButtonImage;
private LayoutData myLastLayoutData;
private boolean myFinishingDrop;
static final int DROP_DISTANCE_SENSITIVITY = 20;
private static final int DROP_DISTANCE_SENSITIVITY = 200;
Stripe(@MagicConstant(valuesFromClass = SwingConstants.class) int anchor) {
Stripe(@MagicConstant(intValues = {SwingConstants.CENTER, SwingConstants.TOP, SwingConstants.LEFT, SwingConstants.BOTTOM, SwingConstants.RIGHT}) int anchor) {
super(new GridBagLayout());
setOpaque(true);
@@ -186,9 +188,9 @@ class Stripe extends JPanel implements UISettingsListener {
data.fitSize = toFitWith != null ? toFitWith : new Dimension();
Rectangle stripeSensitiveRectangle = new Rectangle(-DROP_DISTANCE_SENSITIVITY, -DROP_DISTANCE_SENSITIVITY,
getWidth() + DROP_DISTANCE_SENSITIVITY * 2, getHeight() + DROP_DISTANCE_SENSITIVITY * 2);
boolean processDrop = isDroppingButton() && stripeSensitiveRectangle.intersects(myDropRectangle) && !noDrop;
Point point = myDropRectangle != null ? myDropRectangle.getLocation() : new Point(-1, -1);
SwingUtilities.convertPointToScreen(point, this);
boolean processDrop = isDroppingButton() && containsPoint(point) && !noDrop;
if (toFitWith == null) {
for (StripeButton button : buttons) {
@@ -447,8 +449,8 @@ class Stripe extends JPanel implements UISettingsListener {
}
}
public boolean containsScreen(@NotNull Rectangle screenRec) {
Point point = screenRec.getLocation();
boolean containsPoint(@NotNull Point screenPoint) {
Point point = screenPoint.getLocation();
SwingUtilities.convertPointFromScreen(point, isVisible() ? this : getParent());
int width = getWidth();
int height = getHeight();
@@ -461,12 +463,36 @@ class Stripe extends JPanel implements UISettingsListener {
height = bounds.height;
}
}
return new Rectangle(point, screenRec.getSize()).intersects(
new Rectangle(-DROP_DISTANCE_SENSITIVITY,
-DROP_DISTANCE_SENSITIVITY,
width + 2 * DROP_DISTANCE_SENSITIVITY,
height + 2 * DROP_DISTANCE_SENSITIVITY)
);
int areaSize = Math.min(Math.min(getParent().getWidth() / 2, getParent().getHeight() / 2), JBUI.scale(DROP_DISTANCE_SENSITIVITY));
Point[] points = {new Point(0, 0), new Point(width, 0), new Point(width, height), new Point(0, height)};
switch (anchor) {
case SwingConstants.TOP: {
updateLocation(points, 1, 2, -1, +1, areaSize);
updateLocation(points, 0, 3, 1, 1, areaSize);
break;
}
case SwingConstants.LEFT: {
updateLocation(points, 0, 1, 1, 1, areaSize);
updateLocation(points, 3, 2, 1, -1, areaSize);
break;
}
case SwingConstants.BOTTOM: {
updateLocation(points, 3, 0, 1, -1, areaSize);
updateLocation(points, 2, 1, -1, -1, areaSize);
break;
}
case SwingConstants.RIGHT: {
updateLocation(points, 1, 0, -1, 1, areaSize);
updateLocation(points, 2, 3, -1, 1, areaSize);
}
}
return new Polygon(new int[]{points[0].x, points[1].x, points[2].x, points[3].x},
new int[]{points[0].y, points[1].y, points[2].y, points[3].y}, 4).contains(point);
}
private static void updateLocation(Point[] points, int indexFrom, int indexTo, int xSign, int ySign, int areaSize) {
points[indexTo].setLocation(points[indexFrom].x + xSign * areaSize, points[indexFrom].y + ySign * areaSize);
}
public void finishDrop(@NotNull ToolWindowManagerImpl manager) {

View File

@@ -201,7 +201,7 @@ public class StripeButton extends AnchoredButton implements DataProvider {
myDragButtonImage = new JLabel(IconUtil.createImageIcon((Image)image)) {
@Override
public String toString() {
return "Image for: " + StripeButton.this.toString();
return "Image for: " + StripeButton.this;
}
};
@@ -235,7 +235,7 @@ public class StripeButton extends AnchoredButton implements DataProvider {
SwingUtilities.convertPointToScreen(xy, myDragPane);
Stripe stripe = pane.getStripeFor(new Rectangle(xy, myDragButtonImage.getSize()), (Stripe)getParent());
Stripe stripe = pane.getStripeFor(xy, (Stripe)getParent());
if (stripe == null) {
if (myLastStripe != null) {
myLastStripe.resetDrop();

View File

@@ -89,7 +89,7 @@ internal class ToolWindowDragHelper(parent: @NotNull Disposable,
override fun processMousePressed(event: MouseEvent) {
val relativePoint = RelativePoint(event)
val toolWindow = getToolWindow(relativePoint);
val toolWindow = getToolWindow(relativePoint)
if (toolWindow == null) {
return
}
@@ -150,6 +150,10 @@ internal class ToolWindowDragHelper(parent: @NotNull Disposable,
setDragOut(true)
return
}
if (!willDragOutStart && isWithinInitialHeader(event.locationOnScreen)) {
cancelDragging()
return
}
val window = getToolWindow()
val stripe = myLastStripe
if (window != null) {
@@ -296,12 +300,7 @@ internal class ToolWindowDragHelper(parent: @NotNull Disposable,
dialog.setLocation(screenPoint.x - myInitialOffset.x, screenPoint.y - myInitialOffset.y)
setDragOut(dragOut)
var stripe = myPane.getStripeFor(Rectangle(screenPoint, /*Dimension(dialog.width, dialog.height)*/Dimension(1, 1)),
myPane.getStripeFor(myInitialAnchor!!))
val fallbackBounds = toolWindow.getBoundsOnScreen(myInitialAnchor!!)
if (stripe == null && fallbackBounds.contains(screenPoint)) {
stripe = myPane.getStripeFor(myInitialAnchor!!) //fallback
}
val stripe = getStripe(screenPoint)
if (myLastStripe != null && myLastStripe != stripe) {
myLastStripe!!.resetDrop()
@@ -337,7 +336,7 @@ internal class ToolWindowDragHelper(parent: @NotNull Disposable,
bounds.height -= half
if (dropToSide) {
bounds.y += half
};
}
} else {
bounds.width -= half
if (dropToSide) {
@@ -351,6 +350,17 @@ internal class ToolWindowDragHelper(parent: @NotNull Disposable,
myLastStripe = stripe
}
private fun getStripe(screenPoint: Point): Stripe? {
if (isWithinInitialHeader(screenPoint)) return myPane.getStripeFor(myInitialAnchor!!)
return myPane.getStripeFor(screenPoint, myPane.getStripeFor(myInitialAnchor!!))
}
private fun isWithinInitialHeader(screenPoint: Point): Boolean {
val toolWindow = getToolWindow()
val headerBounds = toolWindow?.decorator?.headerScreenBounds
return (headerBounds != null && headerBounds.contains(screenPoint))
}
private fun setDragOut(dragOut: Boolean) {
myDialog?.setDragOut(dragOut)
myHighlighter.isVisible = !dragOut

View File

@@ -420,17 +420,17 @@ public final class ToolWindowsPane extends JBLayeredPane implements UISettingsLi
}
@Nullable
Stripe getStripeFor(@NotNull Rectangle screenRectangle, @NotNull Stripe preferred) {
if (!new Rectangle(getLocationOnScreen(), getSize()).intersects(screenRectangle)) {
Stripe getStripeFor(@NotNull Point screenPoint, @NotNull Stripe preferred) {
if (!new Rectangle(getLocationOnScreen(), getSize()).contains(screenPoint)) {
return null;
}
if (preferred.containsScreen(screenRectangle)) {
if (preferred.containsPoint(screenPoint)) {
return preferred;
}
for (Stripe stripe : stripes) {
if (stripe.containsScreen(screenRectangle)) {
if (stripe.containsPoint(screenPoint)) {
return stripe;
}
}