You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
9.6 KiB
357 lines
9.6 KiB
/* |
|
* Copyright 2000-2012 JetBrains s.r.o. |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
package com.bulenkov.darcula.ui; |
|
|
|
import com.bulenkov.darcula.util.Animator; |
|
import com.bulenkov.iconloader.util.DoubleColor; |
|
import com.bulenkov.iconloader.util.Gray; |
|
import com.bulenkov.iconloader.util.UIUtil; |
|
|
|
import javax.swing.*; |
|
import javax.swing.event.ChangeEvent; |
|
import javax.swing.plaf.ComponentUI; |
|
import javax.swing.plaf.basic.BasicScrollBarUI; |
|
import java.awt.*; |
|
import java.awt.event.*; |
|
|
|
/** |
|
* @author Konstantin Bulenkov |
|
*/ |
|
public class DarculaScrollBarUI extends BasicScrollBarUI { |
|
|
|
@SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) |
|
public static ComponentUI createUI(JComponent c) { |
|
return new DarculaScrollBarUI(); |
|
} |
|
|
|
public static DoubleColor getGradientLightColor() { |
|
return new DoubleColor(Gray._251, Gray._95); |
|
} |
|
|
|
public static DoubleColor getGradientDarkColor() { |
|
return new DoubleColor(Gray._215, Gray._80); |
|
} |
|
|
|
private static DoubleColor getGradientThumbBorderColor() { |
|
return new DoubleColor(Gray._201, Gray._85); |
|
} |
|
|
|
public static DoubleColor getTrackBackground() { |
|
return new DoubleColor(Gray._245, UIUtil.getListBackground()); |
|
} |
|
|
|
public static DoubleColor getTrackBorderColor() { |
|
return new DoubleColor(Gray._230, UIUtil.getListBackground()); |
|
} |
|
|
|
private static final BasicStroke BORDER_STROKE = new BasicStroke(); |
|
|
|
private static int getAnimationColorShift() { |
|
return UIUtil.isUnderDarcula() ? 20 : 40; |
|
} |
|
|
|
private final AdjustmentListener myAdjustmentListener; |
|
private final MouseMotionAdapter myMouseMotionListener; |
|
private final MouseAdapter myMouseListener; |
|
|
|
private Animator myAnimator; |
|
|
|
private int myAnimationColorShift = 0; |
|
private boolean myMouseIsOverThumb = false; |
|
public static final int DELAY_FRAMES = 4; |
|
public static final int FRAMES_COUNT = 10 + DELAY_FRAMES; |
|
|
|
protected DarculaScrollBarUI() { |
|
myAdjustmentListener = new AdjustmentListener() { |
|
@Override |
|
public void adjustmentValueChanged(AdjustmentEvent e) { |
|
resetAnimator(); |
|
} |
|
}; |
|
|
|
myMouseMotionListener = new MouseMotionAdapter() { |
|
@Override |
|
public void mouseMoved(MouseEvent e) { |
|
boolean inside = isOverThumb(e.getPoint()); |
|
if (inside != myMouseIsOverThumb) { |
|
myMouseIsOverThumb = inside; |
|
resetAnimator(); |
|
} |
|
} |
|
}; |
|
|
|
myMouseListener = new MouseAdapter() { |
|
@Override |
|
public void mouseExited(MouseEvent e) { |
|
if (myMouseIsOverThumb) { |
|
myMouseIsOverThumb = false; |
|
resetAnimator(); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
@Override |
|
public void layoutContainer(Container scrollbarContainer) { |
|
try { |
|
super.layoutContainer(scrollbarContainer); |
|
} catch (NullPointerException ignore) { |
|
//installUI is not performed yet or uninstallUI has set almost every field to null. Just ignore it //IDEA-89674 |
|
} |
|
} |
|
|
|
@Override |
|
protected ModelListener createModelListener() { |
|
return new ModelListener() { |
|
@Override |
|
public void stateChanged(ChangeEvent e) { |
|
if (scrollbar != null) { |
|
super.stateChanged(e); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
public int getDecrementButtonHeight() { |
|
return decrButton.getHeight(); |
|
} |
|
public int getIncrementButtonHeight() { |
|
return incrButton.getHeight(); |
|
} |
|
|
|
private void resetAnimator() { |
|
myAnimator.reset(); |
|
if (scrollbar != null && scrollbar.getValueIsAdjusting() || myMouseIsOverThumb) { |
|
myAnimator.suspend(); |
|
myAnimationColorShift = getAnimationColorShift(); |
|
} |
|
else { |
|
myAnimator.resume(); |
|
} |
|
} |
|
|
|
public static BasicScrollBarUI createNormal() { |
|
return new DarculaScrollBarUI(); |
|
} |
|
|
|
@Override |
|
public void installUI(JComponent c) { |
|
super.installUI(c); |
|
scrollbar.setFocusable(false); |
|
} |
|
|
|
@Override |
|
protected void installDefaults() { |
|
final int incGap = UIManager.getInt("ScrollBar.incrementButtonGap"); |
|
final int decGap = UIManager.getInt("ScrollBar.decrementButtonGap"); |
|
try { |
|
UIManager.put("ScrollBar.incrementButtonGap", 0); |
|
UIManager.put("ScrollBar.decrementButtonGap", 0); |
|
super.installDefaults(); |
|
} |
|
finally { |
|
UIManager.put("ScrollBar.incrementButtonGap", incGap); |
|
UIManager.put("ScrollBar.decrementButtonGap", decGap); |
|
} |
|
} |
|
|
|
@Override |
|
protected void installListeners() { |
|
if (myAnimator == null || myAnimator.isDisposed()) { |
|
myAnimator = createAnimator(); |
|
} |
|
|
|
super.installListeners(); |
|
scrollbar.addAdjustmentListener(myAdjustmentListener); |
|
scrollbar.addMouseListener(myMouseListener); |
|
scrollbar.addMouseMotionListener(myMouseMotionListener); |
|
} |
|
|
|
private Animator createAnimator() { |
|
return new Animator("Adjustment fadeout", FRAMES_COUNT, FRAMES_COUNT * 50, false) { |
|
@Override |
|
public void paintNow(int frame, int totalFrames, int cycle) { |
|
myAnimationColorShift = getAnimationColorShift(); |
|
if (frame > DELAY_FRAMES) { |
|
myAnimationColorShift *= 1 - ((double)(frame - DELAY_FRAMES)) / ((double)(totalFrames - DELAY_FRAMES)); |
|
} |
|
|
|
if (scrollbar != null) { |
|
scrollbar.repaint(((DarculaScrollBarUI)scrollbar.getUI()).getThumbBounds()); |
|
} |
|
} |
|
}; |
|
} |
|
|
|
private boolean isOverThumb(Point p) { |
|
final Rectangle bounds = getThumbBounds(); |
|
return bounds != null && bounds.contains(p); |
|
} |
|
|
|
@Override |
|
public Rectangle getThumbBounds() { |
|
return super.getThumbBounds(); |
|
} |
|
|
|
@Override |
|
protected void uninstallListeners() { |
|
if (scrollTimer != null) { |
|
// it is already called otherwise |
|
super.uninstallListeners(); |
|
} |
|
scrollbar.removeAdjustmentListener(myAdjustmentListener); |
|
myAnimator.dispose(); |
|
} |
|
|
|
@Override |
|
protected void paintTrack(Graphics g, JComponent c, Rectangle bounds) { |
|
g.setColor(getTrackBackground()); |
|
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height); |
|
|
|
g.setColor(getTrackBorderColor()); |
|
if (isVertical()) { |
|
g.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height); |
|
} |
|
else { |
|
g.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y); |
|
} |
|
} |
|
|
|
@Override |
|
protected Dimension getMinimumThumbSize() { |
|
final int thickness = getThickness(); |
|
return isVertical() ? new Dimension(thickness, thickness * 2) : new Dimension(thickness * 2, thickness); |
|
} |
|
|
|
protected int getThickness() { |
|
return 13; |
|
} |
|
|
|
@Override |
|
public Dimension getMaximumSize(JComponent c) { |
|
int thickness = getThickness(); |
|
return new Dimension(thickness, thickness); |
|
} |
|
|
|
@Override |
|
public Dimension getMinimumSize(JComponent c) { |
|
return getMaximumSize(c); |
|
} |
|
|
|
@Override |
|
public Dimension getPreferredSize(JComponent c) { |
|
return getMaximumSize(c); |
|
} |
|
|
|
@Override |
|
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) { |
|
if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) { |
|
return; |
|
} |
|
|
|
g.translate(thumbBounds.x, thumbBounds.y); |
|
paintMaxiThumb((Graphics2D)g, thumbBounds); |
|
g.translate(-thumbBounds.x, -thumbBounds.y); |
|
} |
|
|
|
private void paintMaxiThumb(Graphics2D g, Rectangle thumbBounds) { |
|
final boolean vertical = isVertical(); |
|
int hGap = vertical ? 2 : 1; |
|
int vGap = vertical ? 1 : 2; |
|
|
|
int w = adjustThumbWidth(thumbBounds.width - hGap * 2); |
|
int h = thumbBounds.height - vGap * 2; |
|
|
|
// leave one pixel between thumb and right or bottom edge |
|
if (vertical) { |
|
h -= 1; |
|
} |
|
else { |
|
w -= 1; |
|
} |
|
|
|
final Paint paint; |
|
final Color start = adjustColor(getGradientLightColor()); |
|
final Color end = adjustColor(getGradientDarkColor()); |
|
|
|
if (vertical) { |
|
paint = new GradientPaint(1, 0, start, w + 1, 0, end); |
|
} |
|
else { |
|
paint = new GradientPaint(0, 1, start, 0, h + 1, end); |
|
} |
|
|
|
g.setPaint(paint); |
|
g.fillRect(hGap + 1, vGap + 1, w - 1, h - 1); |
|
|
|
final Stroke stroke = g.getStroke(); |
|
g.setStroke(BORDER_STROKE); |
|
g.setColor(getGradientThumbBorderColor()); |
|
g.drawRoundRect(hGap, vGap, w, h, 3, 3); |
|
g.setStroke(stroke); |
|
} |
|
|
|
@Override |
|
public boolean getSupportsAbsolutePositioning() { |
|
return true; |
|
} |
|
|
|
protected int adjustThumbWidth(int width) { |
|
return width; |
|
} |
|
|
|
protected Color adjustColor(Color c) { |
|
if (myAnimationColorShift == 0) return c; |
|
final int sign = UIUtil.isUnderDarcula() ? -1 : 1; |
|
return Gray.get(Math.max(0, Math.min(255, c.getRed() - sign * myAnimationColorShift))); |
|
} |
|
|
|
private boolean isVertical() { |
|
return scrollbar.getOrientation() == Adjustable.VERTICAL; |
|
} |
|
|
|
@Override |
|
protected JButton createIncreaseButton(int orientation) { |
|
return new EmptyButton(); |
|
} |
|
|
|
@Override |
|
protected JButton createDecreaseButton(int orientation) { |
|
return new EmptyButton(); |
|
} |
|
|
|
private static class EmptyButton extends JButton { |
|
private EmptyButton() { |
|
setFocusable(false); |
|
setRequestFocusEnabled(false); |
|
} |
|
|
|
@Override |
|
public Dimension getMaximumSize() { |
|
return new Dimension(0, 0); |
|
} |
|
|
|
@Override |
|
public Dimension getPreferredSize() { |
|
return getMaximumSize(); |
|
} |
|
|
|
@Override |
|
public Dimension getMinimumSize() { |
|
return getMaximumSize(); |
|
} |
|
} |
|
}
|
|
|