TabbedPane: scroll selected tab into visible area (500ms delayed) if mouse exits scroll viewport after wheel scrolling (issue #40)

This commit is contained in:
Karl Tauber
2020-10-07 13:29:15 +02:00
parent 203426bd55
commit 2f3427e6ad

View File

@@ -46,6 +46,7 @@ import javax.swing.JComponent;
import javax.swing.JTabbedPane; import javax.swing.JTabbedPane;
import javax.swing.JViewport; import javax.swing.JViewport;
import javax.swing.KeyStroke; import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager; import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource; import javax.swing.plaf.UIResource;
@@ -220,6 +221,7 @@ public class FlatTabbedPaneUI
// mouse listener of the tabbed pane would not receive events while // mouse listener of the tabbed pane would not receive events while
// the mouse pointer is over the viewport // the mouse pointer is over the viewport
tabPane.addMouseWheelListener( wheelTabScroller ); tabPane.addMouseWheelListener( wheelTabScroller );
tabPane.addMouseMotionListener( wheelTabScroller );
tabPane.addMouseListener( wheelTabScroller ); tabPane.addMouseListener( wheelTabScroller );
} }
} }
@@ -229,7 +231,10 @@ public class FlatTabbedPaneUI
super.uninstallListeners(); super.uninstallListeners();
if( wheelTabScroller != null ) { if( wheelTabScroller != null ) {
wheelTabScroller.uninstall();
tabPane.removeMouseWheelListener( wheelTabScroller ); tabPane.removeMouseWheelListener( wheelTabScroller );
tabPane.removeMouseMotionListener( wheelTabScroller );
tabPane.removeMouseListener( wheelTabScroller ); tabPane.removeMouseListener( wheelTabScroller );
wheelTabScroller = null; wheelTabScroller = null;
} }
@@ -571,11 +576,20 @@ public class FlatTabbedPaneUI
protected class FlatWheelTabScroller protected class FlatWheelTabScroller
extends MouseAdapter extends MouseAdapter
{ {
private boolean inViewport;
private boolean scrolled;
private Timer timer;
protected void uninstall() {
if( timer != null )
timer.stop();
}
@Override @Override
public void mouseWheelMoved( MouseWheelEvent e ) { public void mouseWheelMoved( MouseWheelEvent e ) {
// because this listener receives mouse events for the whole tabbed pane, // because this listener receives mouse events for the whole tabbed pane,
// we have to check whether the mouse is located over the viewport // we have to check whether the mouse is located over the viewport
if( tabViewport == null || !tabViewport.getBounds().contains( e.getX(), e.getY() ) ) if( !isInViewport( e ) )
return; return;
// compute new view position // compute new view position
@@ -593,19 +607,74 @@ public class FlatTabbedPaneUI
} }
// update view position // update view position
tabViewport.setViewPosition( new Point( x, y ) ); Point newViewPosition = new Point( x, y );
tabViewport.setViewPosition( newViewPosition );
if( !newViewPosition.equals( viewPosition ))
scrolled = true;
updateHover( e ); updateHover( e );
} }
@Override
public void mouseMoved( MouseEvent e ) {
boolean wasInViewport = inViewport;
inViewport = isInViewport( e );
if( inViewport != wasInViewport ) {
if( !inViewport )
viewportExited();
else if( timer != null )
timer.stop();
}
}
@Override
public void mouseExited( MouseEvent e ) {
inViewport = false;
viewportExited();
}
@Override @Override
public void mousePressed( MouseEvent e ) { public void mousePressed( MouseEvent e ) {
// for the case that the tab was only partly visible before the user clicked it // for the case that the tab was only partly visible before the user clicked it
updateHover( e ); updateHover( e );
} }
private void updateHover( MouseEvent e ) { protected boolean isInViewport( MouseEvent e ) {
return (tabViewport != null && tabViewport.getBounds().contains( e.getX(), e.getY() ) );
}
protected void updateHover( MouseEvent e ) {
setRolloverTab( tabForCoordinate( tabPane, e.getX(), e.getY() ) ); setRolloverTab( tabForCoordinate( tabPane, e.getX(), e.getY() ) );
} }
protected void viewportExited() {
if( !scrolled )
return;
if( timer == null ) {
timer = new Timer( 500, e -> ensureSelectedTabVisible() );
timer.setRepeats( false );
}
timer.start();
}
protected void ensureSelectedTabVisible() {
// check whether UI delegate was uninstalled because this method is invoked via timer
if( tabPane == null || tabViewport == null )
return;
if( !scrolled || tabViewport == null )
return;
scrolled = false;
int selectedIndex = tabPane.getSelectedIndex();
if( selectedIndex >= 0 ) {
Rectangle tabBounds = getTabBounds( tabPane, selectedIndex );
tabViewport.scrollRectToVisible( tabBounds );
}
}
} }
} }