VAqua.src.org.violetlib.aqua.AquaScrollBarUI Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaqua Show documentation
Show all versions of vaqua Show documentation
An improved native Swing look and feel for macOS
The newest version!
/*
* Changes Copyright (c) 2015-2021 Alan Snyder.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the license agreement. For details see
* accompanying license terms.
*/
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.violetlib.aqua;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ScrollBarUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.violetlib.jnr.LayoutInfo;
import org.violetlib.jnr.Painter;
import org.violetlib.jnr.aqua.*;
import org.violetlib.jnr.aqua.AquaUIPainter.*;
public class AquaScrollBarUI extends ScrollBarUI implements AquaComponentUI {
public static final String INTERNAL_STYLE_CLIENT_PROPERTY_KEY = "JScrollBar.style";
public static final String INTERNAL_THUMB_STYLE_CLIENT_PROPERTY_KEY = "JScrollBar.thumbStyle";
// As arrows were removed in 10.7, I'm not sure if it is worth keeping code to support them.
public enum ScrollBarPart {
NONE,
THUMB,
TRACK_MIN,
TRACK_MAX,
ARROW_MIN,
ARROW_MAX
}
private static final int kInitialDelay = 300;
private static final int kNormalDelay = 100;
// tracking state
protected float currentThumbPosition;
protected boolean fIsDragging;
protected @Nullable ScrollBarPart hitPart;
protected boolean fisRolloverDisplay; // true to use the rollover display style
protected Timer fScrollTimer;
protected ScrollListener fScrollListener;
protected TrackListener fTrackListener;
protected ScrollBarPart fTrackHighlight = ScrollBarPart.NONE; // not used since Yosemite
protected ScrollBarPart fMousePart = ScrollBarPart.NONE; // not used since Yosemite
protected JScrollBar fScrollBar;
protected ModelListener fModelListener;
protected PropertyChangeListener fPropertyChangeListener;
protected float alpha = 1;
protected final AquaUIPainter painter = AquaPainting.create();
// Create PLAF
public static ComponentUI createUI(JComponent c) {
return new AquaScrollBarUI();
}
public AquaScrollBarUI() { }
@Override
public void installUI(JComponent c) {
fScrollBar = (JScrollBar)c;
installListeners();
configureScrollBarColors();
}
@Override
public void uninstallUI(JComponent c) {
uninstallListeners();
fScrollBar = null;
}
protected TrackListener createTrackListener() {
return new TrackListener();
}
protected ScrollListener createScrollListener() {
return new ScrollListener();
}
protected void installListeners() {
fTrackListener = createTrackListener();
fModelListener = createModelListener();
fPropertyChangeListener = createPropertyChangeListener();
fScrollBar.addMouseListener(fTrackListener);
fScrollBar.addMouseMotionListener(fTrackListener);
fScrollBar.getModel().addChangeListener(fModelListener);
fScrollBar.addPropertyChangeListener(fPropertyChangeListener);
AppearanceManager.installListeners(fScrollBar);
fScrollListener = createScrollListener();
fScrollTimer = new Timer(kNormalDelay, fScrollListener);
fScrollTimer.setInitialDelay(kInitialDelay); // default InitialDelay?
}
protected void uninstallListeners() {
fScrollTimer.stop();
fScrollTimer = null;
AppearanceManager.uninstallListeners(fScrollBar);
fScrollBar.getModel().removeChangeListener(fModelListener);
fScrollBar.removeMouseListener(fTrackListener);
fScrollBar.removeMouseMotionListener(fTrackListener);
fScrollBar.removePropertyChangeListener(fPropertyChangeListener);
}
protected PropertyChangeListener createPropertyChangeListener() {
return new PropertyChangeHandler();
}
protected ModelListener createModelListener() {
return new ModelListener();
}
@Override
public void appearanceChanged(@NotNull JComponent c, @NotNull AquaAppearance appearance) {
}
@Override
public void activeStateChanged(@NotNull JComponent c, boolean isActive) {
}
protected void configureScrollBarColors() {
LookAndFeel.installColors(fScrollBar, "ScrollBar.background", "ScrollBar.foreground");
}
@Override
public void update(Graphics g, JComponent c) {
AppearanceManager.registerCurrentAppearance(c);
super.update(g, c);
}
@Override
public void paint(Graphics g, JComponent c) {
if (alpha == 0) {
return;
}
Graphics2D gg = null;
if (alpha < 1) {
gg = (Graphics2D) g.create();
gg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g = gg;
}
// Since 10.7, there are no arrow buttons, just a thumb in a track.
int width = fScrollBar.getWidth();
int height = fScrollBar.getHeight();
ScrollBarConfiguration bg = getConfiguration(false);
int x = 0;
int y = 0;
// If the scroll bar is an overlay scroll bar in non-rollover mode, we may need to offset the rendering to abut
// the outer edge of the scroll bar
if (bg.getWidget() == ScrollBarWidget.OVERLAY) {
int thickness = getScrollBarThickness(bg);
if (bg.getOrientation() == Orientation.HORIZONTAL) {
y = height - thickness;
height = thickness;
} else if (!AquaScrollPaneUI.isRTLSupported || AquaUtils.isLeftToRight(fScrollBar)) {
x = width - thickness;
width = thickness;
} else {
width = thickness;
}
}
AquaUtils.configure(painter, c, width, height);
Painter p = painter.getPainter(bg);
p.paint(g, x, y);
if (gg != null) {
gg.dispose();
}
}
protected ScrollBarConfiguration getConfiguration(boolean isForLayoutSize) {
ScrollBarWidget sw = getScrollBarWidget(isForLayoutSize);
ScrollBarKnobWidget kw = getScrollBarKnobWidget(sw);
Size size = getScrollBarSize();
State state = getScrollBarState(sw);
Orientation o = getScrollBarOrientation();
float thumbPosition = getCurrentThumbPosition();
float thumbExtent = getCurrentThumbExtent();
// Do not display a thumb if scrolling is not possible
if (thumbExtent >= 0.999) {
kw = ScrollBarKnobWidget.NONE;
}
Object styleProperty = fScrollBar.getClientProperty(INTERNAL_STYLE_CLIENT_PROPERTY_KEY);
boolean noTrack = "sidebar".equals(styleProperty);
return new ScrollBarConfiguration(sw, kw, size, state, o, thumbPosition, thumbExtent, noTrack);
}
/**
* Return the thumb position corresponding to the scroll bar value.
* The thumb position is zero when the scroll bar value is at its minimum.
* The thumb position is one when the scroll bar value is at its maximum.
* If scrolling is not possible (the extent is full or the value range is empty), then the thumb position is zero.
*/
protected float getCurrentThumbPosition() {
float valueRange = Math.max(0, fScrollBar.getMaximum() - fScrollBar.getMinimum());
float scrollingRange = valueRange - fScrollBar.getModel().getExtent();
currentThumbPosition = scrollingRange <= 0 ? 0 : (fScrollBar.getValue() - fScrollBar.getMinimum()) / scrollingRange;
return currentThumbPosition;
}
/**
* Return the scroll bar extent as a fraction of the total range of possible scroll bar values.
* The result is in the range zero to one, inclusive.
* If the value range is empty, the result is zero.
*/
protected float getCurrentThumbExtent() {
float valueRange = Math.max(0, fScrollBar.getMaximum() - fScrollBar.getMinimum());
return valueRange <= 0 ? 0 : fScrollBar.getModel().getExtent() / valueRange;
}
protected int getValueFromThumbPosition(float extendedThumbPosition) {
int minimum = fScrollBar.getMinimum();
int maximum = fScrollBar.getMaximum();
int extent = fScrollBar.getModel().getExtent();
if (extendedThumbPosition <= 0) {
return minimum;
} else if (extendedThumbPosition >= 1) {
return maximum - extent;
} else {
float valueRange = Math.max(0, maximum - minimum);
float scrollingRange = valueRange - extent;
return Math.round(minimum + extendedThumbPosition * scrollingRange);
}
}
protected ScrollBarWidget getScrollBarWidget(boolean isForLayoutSize) {
if (isOverlayStyle()) {
// Use OVERLAY_ROLLOVER for layout because it is wider
return fisRolloverDisplay || isForLayoutSize ? ScrollBarWidget.OVERLAY_ROLLOVER : ScrollBarWidget.OVERLAY;
}
return ScrollBarWidget.LEGACY;
}
protected boolean isOverlayStyle() {
Object o = fScrollBar.getClientProperty(INTERNAL_THUMB_STYLE_CLIENT_PROPERTY_KEY);
if (o instanceof String) {
String style = (String) o;
return style.equals("overlayDark") || style.equals("overlayLight");
}
return false;
}
public boolean isDragging() {
return fIsDragging;
}
protected ScrollBarKnobWidget getScrollBarKnobWidget(ScrollBarWidget sw) {
if (sw == ScrollBarWidget.LEGACY) {
return ScrollBarKnobWidget.DEFAULT; // default is the only option for legacy scroll bars
}
Object o = fScrollBar.getClientProperty(INTERNAL_THUMB_STYLE_CLIENT_PROPERTY_KEY);
if (o instanceof String) {
String style = (String) o;
if (style.equals("overlayLight")) {
return ScrollBarKnobWidget.LIGHT;
}
}
return ScrollBarKnobWidget.DARK;
}
protected Size getScrollBarSize() {
Size sz = AquaUtilControlSize.getUserSizeFrom(fScrollBar);
return sz == Size.REGULAR ? Size.REGULAR : Size.SMALL;
}
protected State getScrollBarState(@NotNull ScrollBarWidget w) {
if (!fScrollBar.isEnabled()) {
return State.DISABLED;
}
if (!AquaFocusHandler.isActive(fScrollBar)) {
return State.INACTIVE;
}
if (fIsDragging) {
return State.PRESSED;
}
if (hitPart != null) {
return State.ROLLOVER;
}
return State.ACTIVE;
}
protected Orientation getScrollBarOrientation() {
return isHorizontal() ? Orientation.HORIZONTAL : Orientation.VERTICAL;
}
/**
* Return the major axis coordinate of the leading edge of the thumb.
*/
protected int getThumbTrackPosition() {
int width = fScrollBar.getWidth();
int height = fScrollBar.getHeight();
ScrollBarConfiguration g = getConfiguration(false);
Rectangle bounds = new Rectangle(0, 0, width, height);
AquaUILayoutInfo uiLayout = painter.getLayoutInfo();
Rectangle2D thumbBounds = uiLayout.getScrollBarThumbBounds(bounds, g);
return (int) (g.getOrientation() == Orientation.VERTICAL ? thumbBounds.getY() : thumbBounds.getX());
}
/**
* Return the scroll part corresponding to a component location. The result is based only on the coordinate of the
* primary axis. Locations outside the bounds of the scroll bar are not given special treatment.
*/
protected ScrollBarPart getPartHit(int x, int y) {
float value = getExtendedTrackPosition(x, y);
if (value < 0) {
return ScrollBarPart.TRACK_MIN;
} else if (value > 1) {
return ScrollBarPart.TRACK_MAX;
} else {
int width = fScrollBar.getWidth();
int height = fScrollBar.getHeight();
AquaUtils.configure(painter, fScrollBar, width, height);
ScrollBarConfiguration g = getConfiguration(false);
boolean isHorizontal = isHorizontal();
int c = isHorizontal ? x : y;
ScrollBarThumbConfiguration tg = new ScrollBarThumbConfiguration(g, c);
int pos = painter.getScrollBarThumbHit(tg);
switch (pos) {
case -1: return ScrollBarPart.TRACK_MIN;
case 0: return ScrollBarPart.THUMB;
case 1: return ScrollBarPart.TRACK_MAX;
default: return ScrollBarPart.NONE;
}
}
}
/**
* Return the position relative to the track length corresponding to a component location. If the location is within
* the track, the returned value is between 0 and 1 (inclusive). If the location is on the low side of the track, a
* negative value is returned. If the location is on the high side of the track, a value greater than 1 is returned.
*/
private float getExtendedTrackPosition(int x, int y) {
int width = fScrollBar.getWidth();
int height = fScrollBar.getHeight();
boolean isHorizontal = isHorizontal();
int c = isHorizontal ? x : y;
ScrollBarWidget sw = getScrollBarWidget(false);
Size size = getScrollBarSize();
Orientation o = getScrollBarOrientation();
float extent = getCurrentThumbExtent();
ScrollBarThumbLayoutConfiguration g = new ScrollBarThumbLayoutConfiguration(sw, size, o, extent, c);
AquaUtils.configure(painter, fScrollBar, width, height);
return painter.getScrollBarThumbPosition(g, false);
}
// Layout Methods
// Layout is controlled by the user in the Appearance Control Panel
// Theme will redraw correctly for the current layout
public void layoutContainer(Container fScrollBarContainer) {
fScrollBar.repaint();
fScrollBar.revalidate();
}
protected Rectangle getTrackBounds() {
return new Rectangle(0, 0, fScrollBar.getWidth(), fScrollBar.getHeight());
}
protected void setMouseHit(@Nullable ScrollBarPart hit) {
if (hit != hitPart) {
hitPart = hit;
fScrollBar.repaint();
}
}
protected void startTimer(boolean initial) {
fScrollTimer.setInitialDelay(initial ? kInitialDelay : kNormalDelay); // default InitialDelay?
fScrollTimer.start();
}
protected void scrollByBlock(int direction) {
synchronized(fScrollBar) {
int oldValue = fScrollBar.getValue();
int blockIncrement = fScrollBar.getBlockIncrement(direction);
int delta = blockIncrement * ((direction > 0) ? +1 : -1);
fScrollBar.setValue(oldValue + delta);
fTrackHighlight = direction > 0 ? ScrollBarPart.TRACK_MAX : ScrollBarPart.TRACK_MIN;
fScrollBar.repaint();
fScrollListener.setDirection(direction);
fScrollListener.setScrollByBlock(true);
}
}
protected void scrollByUnit(int direction) {
synchronized(fScrollBar) {
int delta = fScrollBar.getUnitIncrement(direction);
if (direction <= 0) delta = -delta;
fScrollBar.setValue(delta + fScrollBar.getValue());
fScrollBar.repaint();
fScrollListener.setDirection(direction);
fScrollListener.setScrollByBlock(false);
}
}
protected class PropertyChangeHandler implements PropertyChangeListener {
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
if ("model".equals(propertyName)) {
BoundedRangeModel oldModel = (BoundedRangeModel)e.getOldValue();
BoundedRangeModel newModel = (BoundedRangeModel)e.getNewValue();
oldModel.removeChangeListener(fModelListener);
newModel.addChangeListener(fModelListener);
fScrollBar.repaint();
fScrollBar.revalidate();
} else if (AquaFocusHandler.FRAME_ACTIVE_PROPERTY.equals(propertyName)) {
fScrollBar.repaint();
} else if (INTERNAL_THUMB_STYLE_CLIENT_PROPERTY_KEY.equals(propertyName)) {
fScrollBar.repaint();
}
}
}
protected class ModelListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
layoutContainer(fScrollBar);
}
}
// Track mouse motion
protected class TrackListener extends MouseAdapter implements MouseMotionListener {
protected transient int fCurrentMouseX, fCurrentMouseY;
protected transient boolean fInArrows; // are we currently tracking arrows?
protected transient boolean fStillInArrow = false; // Whether mouse is in an arrow during arrow tracking
protected transient boolean fStillInTrack = false; // Whether mouse is in the track during pageup/down tracking
protected transient int fFirstThumbTrackPosition; // Major axis coordinate of leading edge of thumb at start of drag
protected transient int fHitTrackPosition; // Major axis coordinate of mouse pointer at start of drag
public void mouseReleased(MouseEvent e) {
if (!fScrollBar.isEnabled()) return;
if (fInArrows) {
mouseReleasedInArrows(e);
} else {
mouseReleasedInTrack(e);
}
fInArrows = false;
fStillInArrow = false;
fStillInTrack = false;
fScrollBar.repaint();
fScrollBar.revalidate();
}
@Override
public void mouseEntered(MouseEvent e) {
fisRolloverDisplay = true;
updateHit(e);
fScrollBar.repaint();
}
@Override
public void mouseExited(MouseEvent e) {
setMouseHit(null);
fScrollBar.repaint();
}
@Override
public void mouseMoved(MouseEvent e) {
updateHit(e);
}
protected void updateHit(MouseEvent e) {
if (fScrollBar.isEnabled()) {
ScrollBarPart part = getPartHit(e.getX(), e.getY());
setMouseHit(part);
} else {
setMouseHit(null);
}
}
public void mousePressed(MouseEvent e) {
if (!fScrollBar.isEnabled()) return;
ScrollBarPart part = getPartHit(e.getX(), e.getY());
fInArrows = HitUtil.isArrow(part);
if (fInArrows) {
mousePressedInArrows(e, part);
} else {
if (part == ScrollBarPart.NONE) {
fTrackHighlight = ScrollBarPart.NONE;
} else {
mousePressedInTrack(e, part);
}
}
}
public void mouseDragged(MouseEvent e) {
if (!fScrollBar.isEnabled()) return;
if (fInArrows) {
mouseDraggedInArrows(e);
} else if (fIsDragging) {
mouseDraggedInTrack(e);
} else {
// In pageup/down zones
// check that thumb has not been scrolled under the mouse cursor
ScrollBarPart previousPart = getPartHit(fCurrentMouseX, fCurrentMouseY);
if (!HitUtil.isTrack(previousPart)) {
fStillInTrack = false;
}
fCurrentMouseX = e.getX();
fCurrentMouseY = e.getY();
ScrollBarPart part = getPartHit(e.getX(), e.getY());
boolean temp = HitUtil.isTrack(part);
if (temp == fStillInTrack) return;
fStillInTrack = temp;
if (!fStillInTrack) {
fScrollTimer.stop();
} else {
fScrollListener.actionPerformed(new ActionEvent(fScrollTimer, 0, ""));
startTimer(false);
}
}
}
/**
* Map a mouse coordinate to an extended thumb position.
* @param x The X mouse coordinate.
* @param y The Y mouse coordinate.
* @param useExtent If true, the coordinate is interpreted as the location of the leading edge of the thumb,
* for the purpose of repositioning the thumb. If false, the coordinate is interpreted as
* a fraction of the full track, for the purpose of scroll-to-here.
* @return the extended thumb position (values outside the 0 to 1 range indicate that the coordinate is
* outside the normal range)
*/
int getDragValue(int x, int y, boolean useExtent) {
float valueRange = Math.max(0, fScrollBar.getMaximum() - fScrollBar.getMinimum());
float scrollingRange = valueRange - fScrollBar.getModel().getExtent();
if (scrollingRange <= 0) {
return fScrollBar.getMinimum();
}
ScrollBarWidget sw = getScrollBarWidget(false);
Size size = getScrollBarSize();
float extent = getCurrentThumbExtent();
Orientation o = getScrollBarOrientation();
boolean isHoriz = o == Orientation.HORIZONTAL;
int coordinate = isHoriz ? x : y;
int deltaTrackPosition = coordinate - fHitTrackPosition;
int newThumbTrackPosition = fFirstThumbTrackPosition + deltaTrackPosition;
AquaUtils.configure(painter, fScrollBar, fScrollBar.getWidth(), fScrollBar.getHeight());
ScrollBarThumbLayoutConfiguration g = new ScrollBarThumbLayoutConfiguration(sw, size, o, extent, newThumbTrackPosition);
float newExtendedThumbPosition = painter.getScrollBarThumbPosition(g, useExtent);
return getValueFromThumbPosition(newExtendedThumbPosition);
}
/**
* Arrow Listeners
*/
// Because we are handling both mousePressed and Actions
// we need to make sure we don't fire under both conditions.
// (keyfocus on scrollbars causes action without mousePress
void mousePressedInArrows(MouseEvent e, ScrollBarPart part) {
int direction = HitUtil.isIncrement(part) ? 1 : -1;
fStillInArrow = true;
scrollByUnit(direction);
fScrollTimer.stop();
fScrollListener.setDirection(direction);
fScrollListener.setScrollByBlock(false);
fMousePart = part;
startTimer(true);
}
void mouseReleasedInArrows(MouseEvent e) {
fScrollTimer.stop();
fMousePart = ScrollBarPart.NONE;
fScrollBar.setValueIsAdjusting(false);
}
void mouseDraggedInArrows(MouseEvent e) {
ScrollBarPart whichPart = getPartHit(e.getX(), e.getY());
if ((fMousePart == whichPart) && fStillInArrow) return; // Nothing has changed, so return
if (fMousePart != whichPart && !HitUtil.isArrow(whichPart)) {
// The mouse is not over the arrow we mouse pressed in, so stop the timer and mark as
// not being in the arrow
fScrollTimer.stop();
fStillInArrow = false;
fScrollBar.repaint();
} else {
// We are in the arrow we mouse pressed down in originally, but the timer was stopped so we need
// to start it up again.
fMousePart = whichPart;
fScrollListener.setDirection(HitUtil.isIncrement(whichPart) ? 1 : -1);
fStillInArrow = true;
fScrollListener.actionPerformed(new ActionEvent(fScrollTimer, 0, ""));
startTimer(false);
}
fScrollBar.repaint();
}
void mouseReleasedInTrack(MouseEvent e) {
if (fTrackHighlight != ScrollBarPart.NONE) {
fScrollBar.repaint();
}
fTrackHighlight = ScrollBarPart.NONE;
fIsDragging = false;
fScrollTimer.stop();
fScrollBar.setValueIsAdjusting(false);
}
/**
* Adjust the fScrollBars value based on the result of hitTestTrack
*/
void mousePressedInTrack(MouseEvent e, ScrollBarPart part) {
fScrollBar.setValueIsAdjusting(true);
boolean isHorizontal = isHorizontal();
// If option-click, toggle scroll-to-here
boolean shouldScrollToHere = (part != ScrollBarPart.THUMB) && OSXSystemProperties.isScrollToClick();
if (e.isAltDown()) shouldScrollToHere = !shouldScrollToHere;
// pretend the mouse was dragged from a point in the current thumb to the current mouse point in one big jump
if (shouldScrollToHere) {
fFirstThumbTrackPosition = getThumbTrackPosition();
fHitTrackPosition = isHorizontal ? e.getX() : e.getY();
moveToMouse(e, false);
// OK, now we're in the thumb - any subsequent dragging should move it
fTrackHighlight = ScrollBarPart.THUMB;
fIsDragging = true;
return;
}
fCurrentMouseX = e.getX();
fCurrentMouseY = e.getY();
int direction = 0;
if (part == ScrollBarPart.TRACK_MIN) {
fTrackHighlight = ScrollBarPart.TRACK_MIN;
direction = -1;
} else if (part == ScrollBarPart.TRACK_MAX) {
fTrackHighlight = ScrollBarPart.TRACK_MAX;
direction = 1;
} else {
fFirstThumbTrackPosition = getThumbTrackPosition();
fHitTrackPosition = isHorizontal ? e.getX() : e.getY();
fTrackHighlight = ScrollBarPart.THUMB;
fIsDragging = true;
return;
}
fIsDragging = false;
fStillInTrack = true;
scrollByBlock(direction);
// Check the new location of the thumb
// stop scrolling if the thumb is under the mouse??
ScrollBarPart newPart = getPartHit(fCurrentMouseX, fCurrentMouseY);
if (newPart == ScrollBarPart.TRACK_MIN || newPart == ScrollBarPart.TRACK_MAX) {
fScrollTimer.stop();
fScrollListener.setDirection(((newPart == ScrollBarPart.TRACK_MAX) ? 1 : -1));
fScrollListener.setScrollByBlock(true);
startTimer(true);
}
}
/**
* Set the models value to the position of the top/left
* of the thumb relative to the origin of the track.
*/
void mouseDraggedInTrack(MouseEvent e) {
moveToMouse(e, true);
}
// For normal mouse dragging or click-to-here
// fCurrentMouseX, fCurrentMouseY, and fFirstThumbPosition must be set
void moveToMouse(MouseEvent e, boolean useExtent) {
fCurrentMouseX = e.getX();
fCurrentMouseY = e.getY();
int oldValue = fScrollBar.getValue();
int newValue = getDragValue(fCurrentMouseX, fCurrentMouseY, useExtent);
if (newValue == oldValue) return;
fScrollBar.setValue(newValue);
Rectangle dirtyRect = getTrackBounds();
fScrollBar.repaint(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
}
}
/**
* Listener for scrolling events initiated in the ScrollPane.
*/
protected class ScrollListener implements ActionListener {
boolean fUseBlockIncrement;
int fDirection = 1;
void setDirection(int direction) {
this.fDirection = direction;
}
void setScrollByBlock(boolean block) {
this.fUseBlockIncrement = block;
}
public void actionPerformed(ActionEvent e) {
if (fUseBlockIncrement) {
ScrollBarPart newPart = getPartHit(fTrackListener.fCurrentMouseX, fTrackListener.fCurrentMouseY);
if (newPart == ScrollBarPart.TRACK_MIN || newPart == ScrollBarPart.TRACK_MAX) {
int newDirection = (newPart == ScrollBarPart.TRACK_MAX ? 1 : -1);
if (fDirection != newDirection) {
fDirection = newDirection;
}
}
scrollByBlock(fDirection);
newPart = getPartHit(fTrackListener.fCurrentMouseX, fTrackListener.fCurrentMouseY);
if (newPart == ScrollBarPart.THUMB) {
((Timer)e.getSource()).stop();
}
} else {
scrollByUnit(fDirection);
}
if (fDirection > 0 && fScrollBar.getValue() + fScrollBar.getVisibleAmount() >= fScrollBar.getMaximum()) {
((Timer)e.getSource()).stop();
} else if (fDirection < 0 && fScrollBar.getValue() <= fScrollBar.getMinimum()) {
((Timer)e.getSource()).stop();
}
}
}
@Override
public Dimension getPreferredSize(JComponent c) {
int t = getScrollBarThickness();
return isHorizontal() ? new Dimension(96, t) : new Dimension(t, 96);
}
@Override
public Dimension getMinimumSize(JComponent c) {
int t = getScrollBarThickness();
return isHorizontal() ? new Dimension(54, t) : new Dimension(t, 54);
}
@Override
public Dimension getMaximumSize(JComponent c) {
int t = getScrollBarThickness();
return isHorizontal() ? new Dimension(100000, t) : new Dimension(t, 100000);
}
public int getScrollBarThickness() {
LayoutConfiguration g = getConfiguration(true);
return getScrollBarThickness(g);
}
protected int getScrollBarThickness(LayoutConfiguration g) {
AquaUILayoutInfo uiLayout = painter.getLayoutInfo();
LayoutInfo layoutInfo = uiLayout.getLayoutInfo(g);
float f = isHorizontal() ? layoutInfo.getMinimumVisualHeight() : layoutInfo.getMinimumVisualWidth();
return (int) Math.ceil(f);
}
public void setRolloverDisplayState(boolean b) {
if (fisRolloverDisplay != b) {
fisRolloverDisplay = b;
fScrollBar.repaint();
}
hitPart = null; // may be wrong but does not matter for overlay scroll bars
}
public void setAlpha(float a) {
if (a != alpha) {
alpha = a;
fScrollBar.repaint();
}
}
protected boolean isHorizontal() {
return fScrollBar.getOrientation() == Adjustable.HORIZONTAL;
}
static class HitUtil {
static boolean isIncrement(ScrollBarPart hit) {
return hit == ScrollBarPart.ARROW_MAX;
}
static boolean isDecrement(ScrollBarPart hit) {
return hit == ScrollBarPart.ARROW_MIN;
}
static boolean isArrow(ScrollBarPart hit) {
return isIncrement(hit) || isDecrement(hit);
}
static boolean isTrack(ScrollBarPart hit) {
return (hit == ScrollBarPart.TRACK_MAX) || (hit == ScrollBarPart.TRACK_MIN);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy