mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 15:07:11 -06:00
TabbedPane: use animation for scrolling tabs with mouse wheel (issue #40)
This commit is contained in:
@@ -92,6 +92,14 @@ public interface FlatSystemProperties
|
|||||||
*/
|
*/
|
||||||
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
String MENUBAR_EMBEDDED = "flatlaf.menuBarEmbedded";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies whether animations are enabled.
|
||||||
|
* <p>
|
||||||
|
* <strong>Allowed Values</strong> {@code false} and {@code true}<br>
|
||||||
|
* <strong>Default</strong> {@code true}
|
||||||
|
*/
|
||||||
|
String ANIMATION = "flatlaf.animation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
* Specifies whether vertical text position is corrected when UI is scaled on HiDPI screens.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ import javax.swing.plaf.UIResource;
|
|||||||
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
import javax.swing.plaf.basic.BasicTabbedPaneUI;
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
import com.formdev.flatlaf.FlatLaf;
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.util.Animator;
|
||||||
|
import com.formdev.flatlaf.util.CubicBezierEasing;
|
||||||
import com.formdev.flatlaf.util.UIScale;
|
import com.formdev.flatlaf.util.UIScale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -635,20 +637,33 @@ public class FlatTabbedPaneUI
|
|||||||
private boolean scrolled;
|
private boolean scrolled;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
|
private Animator animator;
|
||||||
|
private Point startViewPosition;
|
||||||
|
private Point targetViewPosition;
|
||||||
|
private int lastMouseX;
|
||||||
|
private int lastMouseY;
|
||||||
|
|
||||||
protected void uninstall() {
|
protected void uninstall() {
|
||||||
if( timer != null )
|
if( timer != null )
|
||||||
timer.stop();
|
timer.stop();
|
||||||
|
if( animator != null )
|
||||||
|
animator.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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( !isInViewport( e ) )
|
if( !isInViewport( e.getX(), e.getY() ) )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
lastMouseX = e.getX();
|
||||||
|
lastMouseY = e.getY();
|
||||||
|
|
||||||
// compute new view position
|
// compute new view position
|
||||||
Point viewPosition = tabViewport.getViewPosition();
|
Point viewPosition = (targetViewPosition != null)
|
||||||
|
? targetViewPosition
|
||||||
|
: tabViewport.getViewPosition();
|
||||||
Dimension viewSize = tabViewport.getViewSize();
|
Dimension viewSize = tabViewport.getViewSize();
|
||||||
int x = viewPosition.x;
|
int x = viewPosition.x;
|
||||||
int y = viewPosition.y;
|
int y = viewPosition.y;
|
||||||
@@ -663,18 +678,68 @@ public class FlatTabbedPaneUI
|
|||||||
|
|
||||||
// update view position
|
// update view position
|
||||||
Point newViewPosition = new Point( x, y );
|
Point newViewPosition = new Point( x, y );
|
||||||
tabViewport.setViewPosition( newViewPosition );
|
setViewPositionAnimated( newViewPosition );
|
||||||
|
|
||||||
if( !newViewPosition.equals( viewPosition ))
|
if( !newViewPosition.equals( viewPosition ))
|
||||||
scrolled = true;
|
scrolled = true;
|
||||||
|
}
|
||||||
|
|
||||||
updateHover( e );
|
protected void setViewPositionAnimated( Point viewPosition ) {
|
||||||
|
// check whether position is equal to current position
|
||||||
|
if( viewPosition.equals( tabViewport.getViewPosition() ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// do not use animation if disabled
|
||||||
|
if( !Animator.useAnimation() ) {
|
||||||
|
tabViewport.setViewPosition( viewPosition );
|
||||||
|
updateHover( lastMouseX, lastMouseY );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember start and target view positions
|
||||||
|
if( startViewPosition == null )
|
||||||
|
startViewPosition = tabViewport.getViewPosition();
|
||||||
|
targetViewPosition = viewPosition;
|
||||||
|
|
||||||
|
// create animator
|
||||||
|
if( animator == null ) {
|
||||||
|
// using same delays as in FlatScrollBarUI
|
||||||
|
int duration = 200;
|
||||||
|
int resolution = 10;
|
||||||
|
|
||||||
|
animator = new Animator( duration, fraction -> {
|
||||||
|
if( tabViewport == null || !tabViewport.isShowing() ) {
|
||||||
|
animator.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update view position
|
||||||
|
int x = startViewPosition.x + Math.round( (targetViewPosition.x - startViewPosition.x) * fraction );
|
||||||
|
int y = startViewPosition.y + Math.round( (targetViewPosition.y - startViewPosition.y) * fraction );
|
||||||
|
tabViewport.setViewPosition( new Point( x, y ) );
|
||||||
|
}, () -> {
|
||||||
|
startViewPosition = targetViewPosition = null;
|
||||||
|
|
||||||
|
if( tabPane != null )
|
||||||
|
updateHover( lastMouseX, lastMouseY );
|
||||||
|
} );
|
||||||
|
|
||||||
|
animator.setResolution( resolution );
|
||||||
|
animator.setInterpolator( new CubicBezierEasing( 0.5f, 0.5f, 0.5f, 1 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// restart animator
|
||||||
|
animator.cancel();
|
||||||
|
animator.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseMoved( MouseEvent e ) {
|
public void mouseMoved( MouseEvent e ) {
|
||||||
|
lastMouseX = e.getX();
|
||||||
|
lastMouseY = e.getY();
|
||||||
|
|
||||||
boolean wasInViewport = inViewport;
|
boolean wasInViewport = inViewport;
|
||||||
inViewport = isInViewport( e );
|
inViewport = isInViewport( e.getX(), e.getY() );
|
||||||
|
|
||||||
if( inViewport != wasInViewport ) {
|
if( inViewport != wasInViewport ) {
|
||||||
if( !inViewport )
|
if( !inViewport )
|
||||||
@@ -693,15 +758,15 @@ public class FlatTabbedPaneUI
|
|||||||
@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.getX(), e.getY() );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isInViewport( MouseEvent e ) {
|
protected boolean isInViewport( int x, int y ) {
|
||||||
return (tabViewport != null && tabViewport.getBounds().contains( e.getX(), e.getY() ) );
|
return (tabViewport != null && tabViewport.getBounds().contains( x, y ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateHover( MouseEvent e ) {
|
protected void updateHover( int x, int y ) {
|
||||||
setRolloverTab( tabForCoordinate( tabPane, e.getX(), e.getY() ) );
|
setRolloverTab( tabForCoordinate( tabPane, x, y ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void viewportExited() {
|
protected void viewportExited() {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.formdev.flatlaf.util;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import javax.swing.Timer;
|
import javax.swing.Timer;
|
||||||
|
import com.formdev.flatlaf.FlatSystemProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
|
* Simple animator based on ideas and concepts from "Filthy Rich Clients" book
|
||||||
@@ -39,6 +40,15 @@ public class Animator
|
|||||||
private long startTime;
|
private long startTime;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether animations are enabled (the default) or disabled via
|
||||||
|
* system property {@code flatlaf.animation} set to {@code false}.
|
||||||
|
* This allows disabling all animations at command line with {@code -Dflatlaf.animation=false}.
|
||||||
|
*/
|
||||||
|
public static boolean useAnimation() {
|
||||||
|
return FlatSystemProperties.getBoolean( FlatSystemProperties.ANIMATION, true );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an animation that runs duration milliseconds.
|
* Creates an animation that runs duration milliseconds.
|
||||||
* Use {@link #addTarget(TimingTarget)} to receive timing events
|
* Use {@link #addTarget(TimingTarget)} to receive timing events
|
||||||
|
|||||||
Reference in New Issue
Block a user