2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.util.ui;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.Disposer;
20 import com.intellij.openapi.util.SystemInfo;
21 import com.intellij.openapi.util.registry.Registry;
22 import com.intellij.ui.Gray;
23 import com.intellij.ui.JBColor;
24 import com.intellij.ui.LightColors;
25 import com.intellij.ui.components.JBScrollPane;
26 import com.intellij.util.Alarm;
27 import com.intellij.util.NotNullProducer;
28 import com.intellij.util.ReflectionUtil;
29 import org.jetbrains.annotations.NotNull;
32 import javax.swing.event.ChangeEvent;
33 import javax.swing.plaf.ScrollBarUI;
34 import javax.swing.plaf.basic.BasicScrollBarUI;
36 import java.awt.event.*;
37 import java.lang.reflect.Method;
41 * @author Konstantin Bulenkov
43 public class ButtonlessScrollBarUI extends BasicScrollBarUI {
44 private static final Logger LOG = Logger.getInstance("#" + ButtonlessScrollBarUI.class.getName());
46 protected JBColor getGradientLightColor() {
47 return jbColor(Gray._251, Gray._95);
50 protected JBColor getGradientDarkColor() {
51 return jbColor(Gray._215, Gray._80);
54 private JBColor getGradientThumbBorderColor() {
55 return jbColor(Gray._201, Gray._85);
58 public static JBColor getTrackBackgroundDefault() {
59 return new JBColor(LightColors.SLIGHTLY_GRAY, UIUtil.getListBackground());
62 public static JBColor getTrackBorderColorDefault() {
63 return new JBColor(Gray._230, UIUtil.getListBackground());
66 private JBColor getTrackBackground() {
67 return jbColor(LightColors.SLIGHTLY_GRAY, UIUtil.getListBackground());
70 private JBColor getTrackBorderColor() {
71 return jbColor(Gray._230, UIUtil.getListBackground());
74 private static final BasicStroke BORDER_STROKE = new BasicStroke();
76 private JBColor jbColor(final Color regular, final Color dark) {
77 return new JBColor(new NotNullProducer<Color>() {
80 public Color produce() {
81 return isDark() ? dark : regular;
86 private int getAnimationColorShift() {
87 return isDark() ? 20 : 40;
90 private final AdjustmentListener myAdjustmentListener;
91 private final MouseMotionAdapter myMouseMotionListener;
92 private final MouseAdapter myMouseListener;
93 private final HierarchyListener myHierarchyListener;
94 private final AWTEventListener myAWTMouseListener;
95 private final NSScrollerHelper.ScrollbarStyleListener myNSScrollerListener;
96 private boolean myGlobalListenersAdded;
98 public static final int DELAY_FRAMES = 4;
99 public static final int FRAMES_COUNT = 10 + DELAY_FRAMES;
101 private Animator myThumbFadeAnimator;
102 private int myThumbFadeColorShift = 0;
104 private boolean myMouseIsOverThumb = false;
105 private boolean myMouseOverScrollbar;
106 private double myMouseOverScrollbarExpandLevel = 0;
108 private NSScrollerHelper.Style myMacScrollerStyle;
109 private Animator myMouseOverScrollbarExpandAnimator;
110 private Alarm myMacScrollbarFadeTimer;
111 private Animator myMacScrollbarFadeAnimator;
112 private double myMacScrollbarFadeLevel = 0;
113 private boolean myMacScrollbarHidden;
115 private ScrollbarRepaintCallback myRepaintCallback;
117 protected ButtonlessScrollBarUI() {
118 myAdjustmentListener = new AdjustmentListener() {
119 Point oldViewportPosition = null;
120 Dimension oldViewportDimension = null;
123 public void adjustmentValueChanged(AdjustmentEvent e) {
124 JScrollPane scrollpane = (JScrollPane)SwingUtilities.getAncestorOfClass(JScrollPane.class, scrollbar);
125 JViewport viewport = scrollpane == null ? null : scrollpane.getViewport();
127 if (viewport == null) {
128 oldViewportPosition = null;
132 boolean vertical = isVertical();
133 Point position = viewport.getViewPosition();
134 // we don't take viewport's size here since it often changes on scrollbar appearance.
135 // instead, we want to only react on visible area resizes
136 Dimension dimension = scrollpane.getSize();
138 boolean scrolled = false;
139 if (oldViewportPosition != null) {
140 int scrollH = position.x - oldViewportPosition.x;
141 int scrollV = position.y - oldViewportPosition.y;
142 scrolled = vertical && scrollH == 0 && scrollV != 0 ||
143 !vertical && scrollV == 0 && scrollH != 0;
145 oldViewportPosition = position;
147 boolean resized = false;
148 if (oldViewportDimension != null) {
149 int resizedH = dimension.width - oldViewportDimension.width;
150 int resizedV = dimension.height - oldViewportDimension.height;
151 resized = vertical && resizedV != 0 || !vertical && resizedH != 0;
153 oldViewportDimension = dimension;
156 // hide the opposite scrollbar when user scrolls
157 JScrollBar other = vertical ? scrollpane.getHorizontalScrollBar()
158 : scrollpane.getVerticalScrollBar();
159 ScrollBarUI otherUI = other == null ? null : other.getUI();
160 if (otherUI instanceof ButtonlessScrollBarUI) {
161 ((ButtonlessScrollBarUI)otherUI).startMacScrollbarFadeout(true);
167 startMacScrollbarFadeout();
172 myMouseMotionListener = new MouseMotionAdapter() {
174 public void mouseMoved(MouseEvent e) {
175 boolean inside = isOverThumb(e.getPoint());
176 if (inside != myMouseIsOverThumb) {
177 myMouseIsOverThumb = inside;
178 startRegularThumbAnimator();
183 myMouseListener = new MouseAdapter() {
185 public void mouseEntered(MouseEvent e) {
186 // only restart animations when fading hasn't started yet
187 if (myMacScrollbarFadeLevel == 0) {
188 myMouseOverScrollbar = true;
189 startMacScrollbarExpandAnimator();
190 startMacScrollbarFadeout();
195 public void mouseExited(MouseEvent e) {
196 if (myMouseIsOverThumb) {
197 myMouseIsOverThumb = false;
198 startRegularThumbAnimator();
201 if (myMouseOverScrollbar) {
202 myMouseOverScrollbar = false;
203 startMacScrollbarExpandAnimator();
204 startMacScrollbarFadeout();
209 myHierarchyListener = new HierarchyListener() {
211 public void hierarchyChanged(HierarchyEvent e) {
212 if (e.getChanged() == scrollbar) {
213 // scrollbar is added to the screen resources
214 if ((HierarchyEvent.DISPLAYABILITY_CHANGED & e.getChangeFlags()) != 0) {
215 updateGlobalListeners(false);
219 if (e.getChanged() == scrollbar.getParent()) {
220 // when scrollpane is shown first time, we 'blink' the scrollbars
221 if ((HierarchyEvent.SHOWING_CHANGED & e.getChangeFlags()) != 0) {
227 myAWTMouseListener = new AWTEventListener() {
228 public void eventDispatched(AWTEvent event) {
229 if (event.getID() == MouseEvent.MOUSE_MOVED) {
231 // user is moving inside the scrollpane of the scrollbar and fade-out hasn't started yet
232 Container scrollpane = SwingUtilities.getAncestorOfClass(JScrollPane.class, scrollbar);
233 if (scrollpane != null) {
234 Point loc = ((MouseEvent)event).getLocationOnScreen();
235 SwingUtilities.convertPointFromScreen(loc, scrollpane);
236 if (scrollpane.contains(loc) && !myMacScrollbarHidden && myMacScrollbarFadeLevel == 0) {
237 startMacScrollbarFadeout();
243 myNSScrollerListener = new NSScrollerHelper.ScrollbarStyleListener() {
245 public void styleChanged() {
246 updateMacScrollbarStyle();
252 protected ArrowButtonListener createArrowButtonListener() {
253 return new ArrowButtonListener() {
255 public void mousePressed(MouseEvent event) {
259 public void mouseReleased(MouseEvent event) {
264 protected boolean isMacOverlayScrollbar() {
265 return myMacScrollerStyle == NSScrollerHelper.Style.Overlay && isMacOverlayScrollbarSupported();
268 public static boolean isMacOverlayScrollbarSupported() {
269 return SystemInfo.isMac && !Registry.is("ide.mac.disableMacScrollbars");
272 private void updateMacScrollbarStyle() {
273 NSScrollerHelper.Style style = NSScrollerHelper.getScrollerStyle();
275 if (style != myMacScrollerStyle && scrollbar != null) {
276 myMacScrollerStyle = style;
278 updateStyleDefaults();
281 JScrollPane pane = JBScrollPane.findScrollPane(scrollbar);
282 if (pane != null) pane.revalidate();
286 public boolean alwaysShowTrack() {
287 return !isMacOverlayScrollbar();
291 public void layoutContainer(Container scrollbarContainer) {
293 super.layoutContainer(scrollbarContainer);
295 catch (NullPointerException ignore) {
296 //installUI is not performed yet or uninstallUI has set almost every field to null. Just ignore it //IDEA-89674
301 * This is overridden only to increase the invalid area.
302 * This ensures that whole track will be repainted in case of installed callback
305 protected void setThumbBounds(int x, int y, int width, int height) {
306 if (width > 0 && height > 0 && UIManager.getBoolean("ScrollBar.alwaysShowThumb") && !alwaysShowTrack()) {
307 int w = scrollbar.getWidth(), h = scrollbar.getHeight();
308 if (w > h && w == width || w < h && h == height) {
309 x = y = width = height = 0;
313 if (myRepaintCallback == null) {
314 super.setThumbBounds(x, y, width, height);
317 // We want to repaint whole scrollbar even if thumb wasn't moved (on small scroll of a big panel)
318 // Even if scrollbar wasn't changed itself, myRepaintCallback could need repaint
320 // Update thumbRect, and repaint the union of x,y,w,h and the old thumbRect.
321 int minX = Math.min(x, trackRect.x);
322 int minY = Math.min(y, trackRect.y);
323 int maxX = Math.max(x + width, trackRect.x + trackRect.width);
324 int maxY = Math.max(y + height, trackRect.y + trackRect.height);
326 thumbRect.setBounds(x, y, width, height);
327 scrollbar.repaint(minX, minY, maxX - minX, maxY - minY);
329 // Once there is API to determine the mouse location this will need to be changed.
330 setThumbRollover(false);
335 protected ModelListener createModelListener() {
336 return new ModelListener() {
338 public void stateChanged(ChangeEvent e) {
339 if (scrollbar != null) {
340 super.stateChanged(e);
346 public int getDecrementButtonHeight() {
347 return Math.max(0, decrButton.getHeight());
349 public int getIncrementButtonHeight() {
350 return Math.max(0, incrButton.getHeight());
353 private void startRegularThumbAnimator() {
354 if (isMacOverlayScrollbar()) return;
356 myThumbFadeAnimator.reset();
357 if (scrollbar != null && scrollbar.getValueIsAdjusting() || myMouseIsOverThumb || Registry.is("ui.no.bangs.and.whistles")) {
358 myThumbFadeAnimator.suspend();
359 myThumbFadeColorShift = getAnimationColorShift();
362 myThumbFadeAnimator.resume();
366 private void startMacScrollbarExpandAnimator() {
367 if (!isMacOverlayScrollbar()) return;
369 if (myMouseOverScrollbarExpandLevel == 0) {
370 myMouseOverScrollbarExpandAnimator.reset();
371 myMouseOverScrollbarExpandAnimator.suspend();
372 if (myMouseOverScrollbar) {
373 myMouseOverScrollbarExpandAnimator.resume();
378 private void startMacScrollbarFadeout() {
379 startMacScrollbarFadeout(false);
382 private void startMacScrollbarFadeout(boolean now) {
383 if (!isMacOverlayScrollbar()) return;
385 myMacScrollbarFadeTimer.cancelAllRequests();
388 if (!myMacScrollbarHidden && !myMacScrollbarFadeAnimator.isRunning()) {
389 myMacScrollbarFadeAnimator.resume();
394 myMacScrollbarFadeAnimator.suspend();
395 myMacScrollbarFadeAnimator.reset();
396 myMacScrollbarHidden = false;
397 myMacScrollbarFadeLevel = 0;
399 JScrollBar sb = scrollbar; // concurrency in background editors initialization
403 if (!myMouseOverScrollbar && !sb.getValueIsAdjusting()) {
404 myMacScrollbarFadeTimer.addRequest(new Runnable() {
407 myMacScrollbarFadeAnimator.resume();
414 public static BasicScrollBarUI createNormal() {
415 return new ButtonlessScrollBarUI();
418 public static BasicScrollBarUI createTransparent() {
419 return new ButtonlessScrollBarUI() {
421 public boolean alwaysShowTrack() {
428 protected void installDefaults() {
429 final int incGap = UIManager.getInt("ScrollBar.incrementButtonGap");
430 final int decGap = UIManager.getInt("ScrollBar.decrementButtonGap");
432 UIManager.put("ScrollBar.incrementButtonGap", 0);
433 UIManager.put("ScrollBar.decrementButtonGap", 0);
434 super.installDefaults();
437 UIManager.put("ScrollBar.incrementButtonGap", incGap);
438 UIManager.put("ScrollBar.decrementButtonGap", decGap);
441 myMacScrollerStyle = NSScrollerHelper.getScrollerStyle();
442 scrollbar.setFocusable(false);
443 updateStyleDefaults();
446 private void updateStyleDefaults() {
447 scrollbar.setOpaque(alwaysShowTrack());
451 protected void installListeners() {
452 initRegularThumbAnimator();
453 initMacScrollbarAnimators();
455 super.installListeners();
456 scrollbar.addAdjustmentListener(myAdjustmentListener);
457 scrollbar.addMouseListener(myMouseListener);
458 scrollbar.addMouseMotionListener(myMouseMotionListener);
460 scrollbar.addHierarchyListener(myHierarchyListener);
461 updateGlobalListeners(false);
466 private void restart() {
467 startRegularThumbAnimator();
468 startMacScrollbarFadeout();
471 private static final Method setValueFrom = ReflectionUtil.getDeclaredMethod(TrackListener.class, "setValueFrom", MouseEvent.class);
473 LOG.assertTrue(setValueFrom != null, "Cannot get TrackListener.setValueFrom method");
477 protected TrackListener createTrackListener() {
478 return new TrackListener() {
480 public void mousePressed(MouseEvent e) {
481 if (scrollbar.isEnabled()
482 && SwingUtilities.isLeftMouseButton(e)
483 && !getThumbBounds().contains(e.getPoint())
484 && NSScrollerHelper.getClickBehavior() == NSScrollerHelper.ClickBehavior.JumpToSpot
485 && setValueFrom != null) {
487 switch (scrollbar.getOrientation()) {
488 case Adjustable.VERTICAL:
489 offset = getThumbBounds().height / 2;
491 case Adjustable.HORIZONTAL:
492 offset = getThumbBounds().width / 2;
497 setValueFrom.invoke(this, e);
499 catch (Exception ex) {
506 super.mousePressed(e);
511 private void updateGlobalListeners(boolean forceRemove) {
512 boolean shouldAdd = scrollbar.isDisplayable();
514 if (myGlobalListenersAdded && (!shouldAdd || forceRemove)) {
515 Toolkit.getDefaultToolkit().removeAWTEventListener(myAWTMouseListener);
516 NSScrollerHelper.removeScrollbarStyleListener(myNSScrollerListener);
517 myGlobalListenersAdded = false;
520 if (!myGlobalListenersAdded && shouldAdd && !forceRemove) {
521 Toolkit.getDefaultToolkit().addAWTEventListener(myAWTMouseListener, AWTEvent.MOUSE_MOTION_EVENT_MASK);
522 NSScrollerHelper.addScrollbarStyleListener(myNSScrollerListener);
523 myGlobalListenersAdded = true;
527 private void initRegularThumbAnimator() {
528 myThumbFadeAnimator = new Animator("Regular scrollbar thumb animator", FRAMES_COUNT, FRAMES_COUNT * 50, false) {
530 public void paintNow(int frame, int totalFrames, int cycle) {
531 myThumbFadeColorShift = getAnimationColorShift();
532 if (frame > DELAY_FRAMES) {
533 myThumbFadeColorShift *= 1 - (double)(frame - DELAY_FRAMES) / (double)(totalFrames - DELAY_FRAMES);
536 if (scrollbar != null) {
537 scrollbar.repaint(((ButtonlessScrollBarUI)scrollbar.getUI()).getThumbBounds());
543 private void initMacScrollbarAnimators() {
544 myMouseOverScrollbarExpandAnimator = new Animator("Mac scrollbar mouse over animator", 10, 200, false) {
546 protected void paintCycleEnd() {
547 myMouseOverScrollbarExpandLevel = 1;
548 if (scrollbar != null) scrollbar.repaint();
552 public void paintNow(int frame, int totalFrames, int cycle) {
553 int delay = totalFrames / 2;
554 int frameAfterDelay = frame - delay;
556 if (frameAfterDelay > 0) {
557 myMouseOverScrollbarExpandLevel = frameAfterDelay / (float)(totalFrames - delay);
558 if (scrollbar != null) scrollbar.repaint();
563 myMacScrollbarFadeTimer = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
564 myMacScrollbarFadeAnimator = new Animator("Mac scrollbar fade animator", 30, 300, false) {
566 protected void paintCycleEnd() {
567 myMacScrollbarHidden = true;
568 myMouseOverScrollbar = false;
569 myMouseOverScrollbarExpandLevel = 0;
571 if (scrollbar != null) scrollbar.repaint();
575 public void paintNow(int frame, int totalFrames, int cycle) {
576 myMacScrollbarFadeLevel = frame / (float)totalFrames;
577 if (scrollbar != null) scrollbar.repaint();
582 private boolean isOverThumb(Point p) {
583 final Rectangle bounds = getThumbBounds();
584 return bounds != null && bounds.contains(p);
588 public Rectangle getThumbBounds() {
589 return super.getThumbBounds();
593 protected void uninstallListeners() {
594 if (scrollTimer != null) {
595 // it is already called otherwise
596 super.uninstallListeners();
599 scrollbar.removeAdjustmentListener(myAdjustmentListener);
600 scrollbar.removeMouseListener(myMouseListener);
601 scrollbar.removeMouseMotionListener(myMouseMotionListener);
603 scrollbar.removeHierarchyListener(myHierarchyListener);
604 updateGlobalListeners(true);
606 Disposer.dispose(myThumbFadeAnimator);
607 Disposer.dispose(myMouseOverScrollbarExpandAnimator);
608 Disposer.dispose(myMacScrollbarFadeTimer);
609 Disposer.dispose(myMacScrollbarFadeAnimator);
613 protected Dimension getMinimumThumbSize() {
614 final int thickness = getThickness();
615 return isVertical() ? new Dimension(thickness, thickness * 2) : new Dimension(thickness * 2, thickness);
618 protected int getThickness() {
619 return isMacOverlayScrollbar() ? JBUI.scale(15) : JBUI.scale(13);
623 public Dimension getMaximumSize(JComponent c) {
624 int thickness = getThickness();
625 return new Dimension(thickness, thickness);
629 public Dimension getMinimumSize(JComponent c) {
630 return getMaximumSize(c);
634 public Dimension getPreferredSize(JComponent c) {
635 return getMaximumSize(c);
639 public boolean contains(JComponent c, int x, int y) {
640 if (isMacOverlayScrollbar() && !alwaysShowTrack() && !alwaysPaintThumb() && myMacScrollbarHidden) return false;
641 return super.contains(c, x, y);
645 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
646 if (alwaysShowTrack() || myMouseOverScrollbarExpandLevel > 0) {
647 doPaintTrack(g, c, trackBounds);
651 protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) {
652 if (isMacOverlayScrollbar() && !alwaysShowTrack()) {
653 bounds = getMacScrollBarBounds(bounds, false);
654 boolean vertical = isVertical();
657 final Color start = adjustColor(UIUtil.getSlightlyDarkerColor(getTrackBackground()));
658 final Color end = adjustColor(getTrackBackground().brighter());
661 paint = UIUtil.getGradientPaint(bounds.x + 1, bounds.y, start, bounds.width + 1, bounds.y, end);
664 paint = UIUtil.getGradientPaint(bounds.x, bounds.y + 1, start, bounds.x, bounds.height + 1, end);
667 Graphics2D g2d = (Graphics2D)g;
669 g2d.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
671 g.setColor(adjustColor(start.darker()));
674 g.setColor(getTrackBackground());
675 g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
677 g.setColor(getTrackBorderColor());
681 g.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height);
684 g.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y);
687 if (myRepaintCallback != null) {
688 myRepaintCallback.call(g);
693 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
694 doPaintThumb(g, thumbBounds);
697 private void doPaintThumb(Graphics g, Rectangle thumbBounds) {
698 if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) {
702 if (isMacOverlayScrollbar()) {
703 paintMacThumb(g, thumbBounds);
706 g.translate(thumbBounds.x, thumbBounds.y);
707 paintMaxiThumb((Graphics2D)g, thumbBounds);
708 g.translate(-thumbBounds.x, -thumbBounds.y);
712 private void paintMacThumb(Graphics g, Rectangle thumbBounds) {
713 if (isMacScrollbarHiddenAndXcodeLikeScrollbar()) return;
715 thumbBounds = getMacScrollBarBounds(thumbBounds, true);
716 Graphics2D g2d = (Graphics2D)g;
717 RenderingHints oldHints = g2d.getRenderingHints();
718 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
720 JBColor baseColor = new JBColor(new NotNullProducer<Color>() {
723 public Color produce() {
724 return !isDark() ? Gray._0 : Gray._128;
728 int arc = Math.min(thumbBounds.width, thumbBounds.height);
730 if (alwaysPaintThumb()) {
731 //noinspection UseJBColor
732 g2d.setColor(new Color(baseColor.getRed(), baseColor.getGreen(), baseColor.getBlue(), isDark() ? 100 : 40));
733 g2d.fillRoundRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height, arc, arc);
734 //g2d.drawRoundRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height, arc, arc);
737 if (!myMacScrollbarHidden) {
738 g2d.setColor(adjustColor(baseColor));
739 g2d.fillRoundRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, thumbBounds.height, arc, arc);
741 g2d.setRenderingHints(oldHints);
744 protected boolean isDark() {
745 return UIUtil.isUnderDarcula();
748 protected boolean alwaysPaintThumb() {
749 return alwaysShowTrack();
752 protected Rectangle getMacScrollBarBounds(Rectangle baseBounds, boolean thumb) {
753 boolean vertical = isVertical();
756 int baseSize = vertical ? baseBounds.width : baseBounds.height;
758 int maxSize = baseSize - (thumb ? borderSize * 2 : 0);
759 int minSize = Math.min(baseSize / 2, 7) + (thumb ? 0 : borderSize * 2);
761 int currentSize = minSize + (int)(myMouseOverScrollbarExpandLevel * (maxSize - minSize));
763 int currentBolderSize = thumb ? borderSize : 0;
765 int x = baseBounds.x;
766 int y = baseBounds.y;
771 x += baseBounds.width - currentSize - currentBolderSize;
772 y += currentBolderSize;
774 height = baseBounds.height - currentBolderSize * 2;
777 x += currentBolderSize;
778 y += baseBounds.height - currentSize - currentBolderSize;
779 width = baseBounds.width - currentBolderSize * 2;
780 height = currentSize;
783 width = Math.max(width, currentSize);
784 height = Math.max(height, currentSize);
786 return new Rectangle(x, y, width, height);
789 protected void paintMaxiThumb(Graphics2D g, Rectangle thumbBounds) {
790 final boolean vertical = isVertical();
791 int hGap = vertical ? 2 : 1;
792 int vGap = vertical ? 1 : 2;
794 int w = thumbBounds.width - hGap * 2;
795 int h = thumbBounds.height - vGap * 2;
797 // leave one pixel between thumb and right or bottom edge
806 final Color start = adjustColor(getGradientLightColor());
807 final Color end = adjustColor(getGradientDarkColor());
810 paint = UIUtil.getGradientPaint(1, 0, start, w + 1, 0, end);
813 paint = UIUtil.getGradientPaint(0, 1, start, 0, h + 1, end);
817 g.fillRect(hGap + 1, vGap + 1, w - 1, h - 1);
819 final Stroke stroke = g.getStroke();
820 g.setStroke(BORDER_STROKE);
821 g.setColor(getGradientThumbBorderColor());
822 final int R = JBUI.scale(3);
823 g.drawRoundRect(hGap, vGap, w, h, R, R);
828 public boolean getSupportsAbsolutePositioning() {
832 protected Color adjustColor(Color c) {
833 if (isMacOverlayScrollbar()) {
834 int alpha = (int)((120 + myMouseOverScrollbarExpandLevel * 20) * (1 - myMacScrollbarFadeLevel));
835 //noinspection UseJBColor
836 return new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
839 if (myThumbFadeColorShift == 0) return c;
840 final int sign = isDark() ? -1 : 1;
841 return Gray.get(Math.max(0, Math.min(255, c.getRed() - sign * myThumbFadeColorShift)));
845 private boolean isVertical() {
846 return scrollbar.getOrientation() == Adjustable.VERTICAL;
850 protected JButton createIncreaseButton(int orientation) {
851 return new EmptyButton();
855 protected JButton createDecreaseButton(int orientation) {
856 return new EmptyButton();
859 protected boolean isMacScrollbarHiddenAndXcodeLikeScrollbar() {
860 return myMacScrollbarHidden && isMacOverlayScrollbarSupported() && xcodeLikeScrollbar();
863 protected static boolean xcodeLikeScrollbar() {
864 return Registry.is("editor.xcode.like.scrollbar");
867 public void registerRepaintCallback(ScrollbarRepaintCallback callback) {
868 myRepaintCallback = callback;
871 private static class EmptyButton extends JButton {
872 private EmptyButton() {
874 setRequestFocusEnabled(false);
878 public Dimension getMaximumSize() {
879 return JBUI.emptySize();
883 public Dimension getPreferredSize() {
884 return getMaximumSize();
888 public Dimension getMinimumSize() {
889 return getMaximumSize();
893 public interface ScrollbarRepaintCallback {
894 void call(Graphics g);