Skip to content

Commit

Permalink
topcat: add navigation help panel
Browse files Browse the repository at this point in the history
Navigators now report the mouse gestures which influence them.
Topcat in turn displays this information in a panel at the bottom of
the plot window.
  • Loading branch information
mbtaylor authored and mmpcn committed Nov 27, 2014
1 parent 2dfc752 commit 7297d41
Show file tree
Hide file tree
Showing 18 changed files with 478 additions and 6 deletions.
11 changes: 11 additions & 0 deletions topcat/src/main/uk/ac/starlink/topcat/AuxWindow.java
Expand Up @@ -380,6 +380,17 @@ public JPanel getControlPanel() {
return controlPanel;
}

/**
* Returns the panel containing the body of this window.
* This contains most of the content but not the parts that have to
* go at the top and bottom like the toolbar and progress bar.
*
* @return body panel
*/
public JComponent getBodyPanel() {
return overPanel;
}

/**
* Obtains simple confirmation from a user.
* This is just a convenience method wrapping a JOptionPane invocation.
Expand Down
155 changes: 155 additions & 0 deletions topcat/src/main/uk/ac/starlink/topcat/plot2/NavigationHelpPanel.java
@@ -0,0 +1,155 @@
package uk.ac.starlink.topcat.plot2;

import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import uk.ac.starlink.ttools.plot2.Gesture;

/**
* Provides a description of the available navigation gestures.
*
* @author Mark Taylor
* @since 3 Feb 2014
*/
public class NavigationHelpPanel extends JPanel {

private final JComponent helpBox_;
private final JComponent legendBox_;
private final Map<Gesture,String> optMap_;
private final Map<Gesture,JLabel> labelMap_;
private final Map<Gesture,Dimension> sizeMap_;
private static final int PAD = 16;

/**
* Constructor.
*/
public NavigationHelpPanel() {
setLayout( new BoxLayout( this, BoxLayout.X_AXIS ) );
helpBox_ = Box.createHorizontalBox();
legendBox_ = Box.createHorizontalBox();
optMap_ = new HashMap<Gesture,String>();
labelMap_ = new LinkedHashMap<Gesture,JLabel>();
sizeMap_ = new HashMap<Gesture,Dimension>();
add( helpBox_ );

/* If the mouse enters this component, its display switches to a
* description of the icons used here. This idea was pinched
* from the GAIA UI. */
addMouseListener( new MouseAdapter() {
public void mouseEntered( MouseEvent evt ) {
removeAll();
add( legendBox_ );
revalidate();
repaint();
}
public void mouseExited( MouseEvent evt ) {
removeAll();
add( helpBox_ );
revalidate();
repaint();
}
} );
}

/**
* Sets the list of known gestures and their meanings.
*
* @param optMap ordered mapping of gestures to behaviour descriptions
*/
public void setOptions( Map<Gesture,String> optMap ) {

/* If the map is just the same as last time, there is no need to
* update the appearance. */
if ( optMap_.equals( optMap ) ) {
return;
}
else {
optMap_.clear();
optMap_.putAll( optMap );
}

/* If the gesture list is the same as last time, change the text
* but try to keep the placements unchanged, since flickering
* repositioning of the icons and text is visually distracting.
* Only resize if any of the components does not have enough
* room to display (i.e. resize if too big, but not if too small). */
Set<Gesture> gestures = optMap.keySet();
if ( gestures.equals( labelMap_.keySet() ) ) {
boolean resize = false;
for ( Gesture gesture : gestures ) {
JLabel label = labelMap_.get( gesture );
label.setPreferredSize( null );
label.setText( optMap.get( gesture ) );
Dimension pSize = label.getPreferredSize();
Dimension maxSize = sizeMap_.get( gesture );
if ( pSize.width > maxSize.width ||
pSize.height > maxSize.height ) {
sizeMap_.put( gesture, new Dimension( pSize ) );
resize = true;
}
Dimension size = new Dimension( sizeMap_.get( gesture ) );
label.setMinimumSize( new Dimension( size ) );
label.setPreferredSize( new Dimension( size ) );
}
if ( resize ) {
helpBox_.revalidate();
}
}

/* Otherwise, all change. Create a new component to display the
* gesture meanings, and another (legend) to display the meanings
* of the icons. */
else {
helpBox_.removeAll();
legendBox_.removeAll();
legendBox_.add( new JLabel( "Mouse Help: " ) );
labelMap_.clear();
sizeMap_.clear();
for ( Gesture gesture : gestures ) {
String txt = optMap.get( gesture );
JLabel label =
new JLabel( txt, gesture.getIcon(), SwingConstants.LEFT );
helpBox_.add( label );
Dimension size = label.getPreferredSize();
sizeMap_.put( gesture, new Dimension( size ) );
label.setMinimumSize( new Dimension( size ) );
label.setPreferredSize( new Dimension( size ) );
labelMap_.put( gesture, label );
helpBox_.add( Box.createHorizontalStrut( PAD ) );
legendBox_.add( new JLabel( ": " + gesture.getDescription(),
gesture.getIcon(),
SwingConstants.LEFT ) );
legendBox_.add( Box.createHorizontalStrut( PAD ) );
}
helpBox_.add( Box.createHorizontalGlue() );
helpBox_.revalidate();
legendBox_.add( Box.createHorizontalGlue() );
legendBox_.revalidate();
}
repaint();
}

/**
* Disabling the component greys it out as usual.
* The semantics of this are a bit different from usual; user interaction
* is not really possible anyway, but if you want to grey it out,
* this method works anyway.
*/
@Override
public void setEnabled( boolean enabled ) {
super.setEnabled( enabled );
for ( JLabel label : labelMap_.values() ) {
label.setEnabled( enabled );
}
}
}
85 changes: 80 additions & 5 deletions topcat/src/main/uk/ac/starlink/topcat/plot2/StackPlotWindow.java
Expand Up @@ -25,6 +25,7 @@
import java.util.logging.Logger;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JComponent;
Expand All @@ -49,6 +50,7 @@
import uk.ac.starlink.ttools.plot.Range;
import uk.ac.starlink.ttools.plot2.AuxScale;
import uk.ac.starlink.ttools.plot2.DataGeom;
import uk.ac.starlink.ttools.plot2.Gesture;
import uk.ac.starlink.ttools.plot2.NavigationListener;
import uk.ac.starlink.ttools.plot2.Navigator;
import uk.ac.starlink.ttools.plot2.PlotLayer;
Expand Down Expand Up @@ -95,6 +97,7 @@ public class StackPlotWindow<P,A> extends AuxWindow {
private final ToggleButtonModel showProgressModel_;
private final JLabel posLabel_;
private final JLabel countLabel_;
private final NavigationHelpPanel navPanel_;
private final BlobPanel2 blobPanel_;
private final Action blobAction_;
private final Action fromVisibleAction_;
Expand Down Expand Up @@ -188,6 +191,14 @@ public void setAspect( A aspect ) {
}
}.addListeners( plotPanel_ );

/* Arrange to update the GUI appropriately when the user moves the
* mouse around. */
axisController_.addActionListener( new ActionListener() {
public void actionPerformed( ActionEvent evt ) {
updatePositionDisplay( plotPanel_.getMousePosition() );
}
} );

/* Arrange for user clicks to identify points. */
plotPanel_.addMouseListener( new IdentifyListener() );

Expand All @@ -197,15 +208,15 @@ public void setAspect( A aspect ) {
posLine.setBorder( BorderFactory.createEtchedBorder() );
plotPanel_.addMouseListener( new MouseAdapter() {
public void mouseEntered( MouseEvent evt ) {
displayPosition( evt.getPoint() );
updatePositionDisplay( evt.getPoint() );
}
public void mouseExited( MouseEvent evt ) {
displayPosition( null );
updatePositionDisplay( null );
}
} );
plotPanel_.addMouseMotionListener( new MouseMotionAdapter() {
public void mouseMoved( MouseEvent evt ) {
displayPosition( evt.getPoint() );
updatePositionDisplay( evt.getPoint() );
}
} );

Expand Down Expand Up @@ -409,14 +420,19 @@ public void actionPerformed( ActionEvent evt ) {
statusLine.add( posLine );
statusLine.add( countLine );
JComponent cpanel = getControlPanel();
cpanel.setLayout( new BoxLayout( cpanel, BoxLayout.X_AXIS ) );
cpanel.setLayout( new BoxLayout( cpanel, BoxLayout.Y_AXIS ) );
cpanel.add( statusLine );
navPanel_ = new NavigationHelpPanel();
navPanel_.setBorder( BorderFactory.createEmptyBorder( 2, 0, 0, 0 ) );
cpanel.add( navPanel_ );

/* Set default component dimensions. */
displayPanel.setMinimumSize( new Dimension( 150, 150 ) );
displayPanel.setPreferredSize( new Dimension( 500, 400 ) );
stackPanel.setMinimumSize( new Dimension( 200, 100 ) );
stackPanel.setPreferredSize( new Dimension( 500, 240 ) );
getBodyPanel().setBorder( BorderFactory
.createEmptyBorder( 10, 10, 2, 10 ) );

/* Place the plot and control components. */
getMainArea().setLayout( new BorderLayout() );
Expand Down Expand Up @@ -971,7 +987,7 @@ private void applyMasks( Map<TopcatModel,BitSet> maskMap ) {
private void plotChanged() {

/* Update position immediately. */
displayPosition( plotPanel_.getMousePosition() );
updatePositionDisplay( plotPanel_.getMousePosition() );

/* Work out if it makes any sense to do a blob or visibility
* selection. */
Expand All @@ -998,6 +1014,18 @@ public void run() {
} );
}

/**
* This method is called when the mouse position changes in the plot panel.
*
*
* @param pos current (new) mouse position, or null if the mouse
* is positioned outside the bounds of the PlotPanel
*/
private void updatePositionDisplay( Point pos ) {
displayPosition( pos );
displayNavHelp( pos );
}

/**
* Displays the formatted position at a given point in the status panel.
*
Expand All @@ -1018,6 +1046,53 @@ private void displayPosition( Point point ) {
posLabel_.setText( pos );
}

/**
* Ensures that navigation help is displayed correctly for the current
* cursor position.
*
* @param pos cursor position, or null if outside the plot panel
*/
private void displayNavHelp( Point pos ) {
Surface surface = plotPanel_.getSurface();
final Map<Gesture,String> navOpts;
final boolean active;
if ( surface != null ) {
Navigator<A> navigator = getNavigator();

/* Get a notional position for the navigation help to refer to.
* If the reported position is within the plot panel, use that.
* If it's outside the component altogether, use a position which
* is within the actual bounds of the plot (inside the axes).
* Navigation options may be different outside the plot bounds
* (e.g. below the X axis or left of the Y axis). */
boolean inBounds = pos != null
&& plotPanel_.getBounds().contains( pos );
final Point pos1;
if ( inBounds ) {
pos1 = pos;
}
else {
Point origin = surface.getPlotBounds().getLocation();
pos1 = new Point( origin.x + 1, origin.y + 1 );
}
active = inBounds;

/* Add an item referring to the point selection provided by the
* mouse listener added by this window. */
navOpts = new LinkedHashMap<Gesture,String>();
navOpts.put( Gesture.CLICK_1, "Select" );
navOpts.putAll( navigator.getNavOptions( surface, pos1 ) );
}
else {
active = false;
navOpts = new HashMap<Gesture,String>();
}

/* Update the panel. */
navPanel_.setOptions( navOpts );
navPanel_.setEnabled( active );
}

/**
* Indicates whether any of the submitted list of plot layers
* makes use of a colour scale.
Expand Down
7 changes: 7 additions & 0 deletions ttools/src/main/uk/ac/starlink/ttools/gui/ResourceIcon.java
Expand Up @@ -48,6 +48,13 @@ public class ResourceIcon {
MODE_ALPHA = readIcon( "mode-transparent.gif" ),
MODE_ALPHA_FIX = readIcon( "mode-transparent-lock.gif" ),
MODE_AUX = readIcon( "mode-aux.gif" ),
DRAG1 = readIcon( "drag1.gif" ),
DRAG2 = readIcon( "drag2.gif" ),
DRAG3 = readIcon( "drag3.gif" ),
CLICK1 = readIcon( "click1.gif" ),
CLICK2 = readIcon( "click2.gif" ),
CLICK3 = readIcon( "click3.gif" ),
MOUSE_WHEEL = readIcon( "mwheel.gif" ),

/* Placeholder and terminator. */
DO_WHAT = readIcon( "burst.gif" );
Expand Down

0 comments on commit 7297d41

Please sign in to comment.