GP-875 - Popup window placement integration

This commit is contained in:
dragonmacher
2021-06-03 11:18:24 -04:00
parent d7eb2004a1
commit f978164cfc
6 changed files with 78 additions and 27 deletions

View File

@@ -192,9 +192,20 @@ public abstract class AbstractHoverProvider implements HoverProvider {
popupWindow.showPopup(event);
}
else {
int xOffset = 50;// magic: trial and error
Dimension size = fieldBounds.getSize();
Dimension keepVisibleArea = new Dimension(xOffset, size.height);
//
// Make an area over which to show the popup. The popup should not cover this area.
// The field that is hovered may be too big to be this area, as a big field may cause
// the popup to be too far away from the cursor.
//
// Use the mouse point and then create an area (based on trial-and-error) that should
// not be occluded.
//
int horizontalPad = 100;
int verticalPad = 50;
Rectangle keepVisibleArea = new Rectangle(event.getPoint());
keepVisibleArea.grow(horizontalPad, verticalPad);
popupWindow.showOffsetPopup(event, keepVisibleArea);
}
}

View File

@@ -25,10 +25,16 @@ import java.util.List;
import javax.swing.*;
import javax.swing.Timer;
import docking.widgets.shapes.*;
import generic.util.WindowUtilities;
import ghidra.util.bean.GGlassPane;
import ghidra.util.bean.GGlassPanePainter;
/**
* A generic window intended to be used as a temporary window to show information. This window is
* designed to stay open as long as the user mouses over the window. Once the user mouses away,
* the window will be closed.
*/
public class PopupWindow {
private static final int X_PADDING = 25;
private static final int Y_PADDING = 25;
@@ -302,15 +308,12 @@ public class PopupWindow {
// painters.add(painter);
}
// for debug
// private static List<GGlassPanePainter> painters = new ArrayList<>();
//==================================================================================================
// Inner Classes
//==================================================================================================
// for debug
//private static List<GGlassPanePainter> painters = new ArrayList<>();
// private static List<GGlassPanePainter> painters = new ArrayList<>();
/** Paints shapes used by this class (useful for debugging) */
@SuppressWarnings("unused")

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets;
package docking.widgets.shapes;
/**
* Specifies location and metrics for {@link PopupWindowPlacer}.

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets;
package docking.widgets.shapes;
import java.awt.*;
@@ -134,6 +134,8 @@ import ghidra.util.exception.AssertException;
* This last placer, when chosen, serves as an assert condition, which is helpful
* in circumstances where the developer believes such an assertion is not possible,
* such as when allowing an overlapping placement solution.
*
* @see PopupWindowPlacerBuilder
*/
public abstract class PopupWindowPlacer {
@@ -144,10 +146,10 @@ public abstract class PopupWindowPlacer {
private PopupWindowPlacer next = null;
/**
* Constructor only for classes that do you use Preferences
* Constructor only for classes that do not use placement preferences
*/
public PopupWindowPlacer() {
// Only for implementations that do not use Preferences
// Only for implementations that do not use placement preferences
}
/**
@@ -156,6 +158,8 @@ public abstract class PopupWindowPlacer {
* @param major edge
* @param minorBegin start location on edge
* @param minorEnd end location on edge
*
* @see PopupWindowPlacerBuilder
*/
public PopupWindowPlacer(Location major, Location minorBegin, Location minorEnd) {
if (major == Location.CENTER) {
@@ -263,7 +267,7 @@ public abstract class PopupWindowPlacer {
* and the minor axis being vertical; the major axis being vertical, and the minor axis being
* horizontal. These two situations are independent, but the same algorithm is used.
* <p>
* <B>Algorithm Design...</B><p>
* <B>Algorithm Design</B><p>
* Note: smaller values are up and bigger values are down, in the presentation below.
* <p>
* In trying to allay some confusion (yes it can be confusing), note that for any given major
@@ -283,7 +287,7 @@ public abstract class PopupWindowPlacer {
* Keeping with the original right edge scenario begun above, we are trying to find a minor
* axis placement on the right (major) edge. For this placement, one can refer to
* documentation elsewhere in this class, but essentially, we are trying to place a popup
* area against a context rectangle without exceeded the outer bounds (screen) rectangle. But,
* area against a context rectangle without exceeding the outer bounds (screen) rectangle. But,
* again, we are only considering the placement against the right edge already chosen and only
* trying to fit in the vertical dimension against this edge. Thus, this algorithm only needs
* values for this one dimension. These are the length of the placement area in this one
@@ -348,7 +352,7 @@ public abstract class PopupWindowPlacer {
* else
* no solution
* </pre>
* ... or...
* or
* <pre>
* <B>lesserLocation</B>
* <B>&rarr;</B> <B>lesserBoundedLocation</B> (&ge; <B>lesserLocation</B>)
@@ -369,7 +373,7 @@ public abstract class PopupWindowPlacer {
* else
* solution is centerLocation
* </pre>
* ... or...
* or
* <pre>
* <B>lesserLocation</B>
* <B>centerLocation</B>

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets;
package docking.widgets.shapes;
import docking.widgets.PopupWindowPlacer.*;
import docking.widgets.shapes.PopupWindowPlacer.*;
/**
* This class builds a PopWindowPlacer that can have subsequent PopWindowPlacers.
@@ -24,7 +24,11 @@ import docking.widgets.PopupWindowPlacer.*;
* placers, and a clean-up <B>assert</B> placer. Additionally, there are <B>rotational</B> placers
* that are composed of edge placers.
* <p>
* <B>EDGE PLACERS</B>
* <BR>
* <BR>
*
* <H1>Edge Placers</H1>
*
* <p>
* The <B>edge</B> placers are the leftEdge, rightEdge, topEdge, and bottomEdge methods that take
* Location arguments that one can think of as "cells" for optimal placement, but which have some
@@ -38,7 +42,10 @@ import docking.widgets.PopupWindowPlacer.*;
* same way for each method, though some arguments are not valid for some edges; for instance,
* <code>Location.TOP</code> is only valid for left and right edges.
* <p>
* <B>Two or More Location Arguments</B><p>
*
* <H2>Two or More Location Arguments</H2>
*
* <p>
* When two or more arguments are used, the first argument specifies the nominal placement cell
* and the second argument specifies how far the solution is allowed to shift. If a solution is
* not found and if there are more than two arguments, another placement attempt is made where
@@ -46,7 +53,10 @@ import docking.widgets.PopupWindowPlacer.*;
* far the solution is allowed to shift. To specify a "no-shift" solution, one specifies the same
* placement cell twice (e.g., <code>rightEdge(Location.TOP, Location.TOP)</code>).
* <p>
* <B>One Location Argument</B><p>
*
* <H2>One Location Argument</H2>
*
* <p>
* When one argument is used, the solution is the same as when two arguments are specified except
* that the second argument is automatically set to the nearest neighboring cell. Thus,
* <code>rightEdge(Location.TOP)</code> is the same as
@@ -54,11 +64,17 @@ import docking.widgets.PopupWindowPlacer.*;
* <code>Location.CENTER</code>, two attempts are built, the first being the BOTTOM or RIGHT cell
* and the second being the TOP or LEFT cell.
* <p>
* <B>No Arguments</B><p>
*
* <H2>No Arguments</H2>
*
* <p>
* When no arguments are specified, two arguments to the underlying placer are automatically set
* to BOTTOM or RIGHT for the first and TOP or LEFT for the second.
* <p>
* <B>Examples</B><p>
*
* <H2>Examples</H2>
*
* <p>
* Builds a placer that first attempts a placement at the bottom of the right edge with no
* shift, then tries the top of the right edge with no shift, then top center with no shift:
* <pre>
@@ -77,7 +93,11 @@ import docking.widgets.PopupWindowPlacer.*;
* .topEdge(Location.CENTER);
* .build();</pre>
* <p>
* <B>ROTATIONAL PLACERS</B>
* <BR>
* <BR>
*
* <H1>Rotational Placers</H1>
*
* <p>
* There are clockwise and counter-clockwise rotational placers that built up from edge placers.
* These are:
@@ -96,7 +116,11 @@ import docking.widgets.PopupWindowPlacer.*;
* except that they start at the "beginning" cell where the most previous placer had left off. If
* there was not a previous placer, then the BOTTOM RIGHT cell is chosen as the starting cell.
* <p>
* <B>OVERLAPPING CORNER PLACER</B>
* <BR>
* <BR>
*
* <H1>Overlapping Corner Placer</H1>
*
* <p>
* There is one corner placer, <code>leastOverlapCorner()</code>. This placer tries to make a
* placement at each of the corners of the context area and shifts into the context region as much
@@ -106,7 +130,11 @@ import docking.widgets.PopupWindowPlacer.*;
* top left. Unless ill-constructed (sized of context area, screen, and pop-up dimension), this
* placer should always find a solution.
* <p>
* <B>ASSERT PLACER</B>
* <BR>
* <BR>
*
* <H1>Assert Placer</H1>
*
* <p>
* The <code>throwsAssertException()</code> placer is available, which automatically throws an
* AssertException. This placer is only intended to be used by the client in such as case when
@@ -114,7 +142,12 @@ import docking.widgets.PopupWindowPlacer.*;
* <code>leastOverlapCorner()</code> placer. This just throws an exception instead of returning
* the <code>null</code> return value that would be returned from previous placement attempts.
* <p>
* <B>COMPOSITE EXAMPLES</B><p>
* <BR>
* <BR>
*
* <H1>Composite Placer</H1>
*
* <p>
* Builds a placer that first attempts a placement at the right edge from bottom to top, then
* left edge from bottom to top, then top edge from right to left, then bottom edge from right to
* left, followed by a least-overlap-corner solution, followed by a failure assert:

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package docking.widgets;
package docking.widgets.shapes;
import static org.junit.Assert.*;