Platform: OS X scrollbars (taking system preference into account IDEA-122633)

This commit is contained in:
Anton Makeev
2014-03-24 11:11:17 +01:00
parent d051e6b015
commit cd869fbabd
5 changed files with 192 additions and 32 deletions
+1
View File
@@ -36,6 +36,7 @@
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" name="http-client" level="project" />
<orderEntry type="module" module-name="structure-view-api" exported="" />
<orderEntry type="library" name="jna" level="project" />
</component>
</module>
@@ -64,8 +64,10 @@ public class JBScrollPane extends JScrollPane {
if (c == null) return null;
if (!(c instanceof JViewport)) {
c = c.getParent();
if (!(c instanceof JViewport)) return null;
// if asked for a viewport child, take a viewport.
// If not (e.g asked for a scrollbar), go straight to JLayeredPane
Container vp = c.getParent();
if (vp instanceof JViewport) c = vp;
}
c = c.getParent();
@@ -16,10 +16,13 @@
package com.intellij.util.ui;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.ui.Gray;
import com.intellij.ui.JBColor;
import com.intellij.ui.LightColors;
import com.intellij.ui.components.JBScrollPane;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
@@ -64,7 +67,8 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
private final MouseAdapter myMouseListener;
private final HierarchyListener myHierarchyListener;
private final AWTEventListener myAWTMouseListener;
private boolean myAWTMouseListenerAdded;
private final NSScrollerHelper.ScrollbarStyleListener myNSScrollerListener;
private boolean myGlobalListenersAdded;
public static final int DELAY_FRAMES = 4;
public static final int FRAMES_COUNT = 10 + DELAY_FRAMES;
@@ -76,6 +80,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
private boolean myMouseOverScrollbar;
private double myMouseOverScrollbarExpandLevel = 0;
private NSScrollerHelper.Style myMacScrollerStyle;
private Animator myMouseOverScrollbarExpandAnimator;
private Animator myMacScrollbarFadeAnimator;
private double myMacScrollbarFadeLevel = 0;
@@ -131,9 +136,15 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
myHierarchyListener = new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if (e.getChanged() == scrollbar
&& (HierarchyEvent.DISPLAYABILITY_CHANGED & e.getChangeFlags()) != 0) {
updateAWTMotionListener(false);
if (e.getChanged() == scrollbar) {
if ((HierarchyEvent.DISPLAYABILITY_CHANGED & e.getChangeFlags()) != 0) {
updateGlobalListeners(false);
}
if ((HierarchyEvent.SHOWING_CHANGED & e.getChangeFlags()) != 0) {
resetRegularThumbAnimator();
resetMacScrollbarFadeout();
}
}
}
};
@@ -153,14 +164,38 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
}
};
}
protected static boolean isMacScrollbar() {
return UIUtil.isUnderNativeMacLookAndFeel() && !Registry.is("ui.no.mac.scrollbar");
myNSScrollerListener = new NSScrollerHelper.ScrollbarStyleListener() {
@Override
public void styleChanged(@NotNull NSScrollerHelper.Style newStyle) {
updateMacScrollbarStyle();
}
};
}
protected boolean isMacOverlayScrollbar() {
return myMacScrollerStyle == NSScrollerHelper.Style.Overlay;
}
private void updateMacScrollbarStyle() {
if (!SystemInfo.isMac) return;
NSScrollerHelper.Style style = UIUtil.isUnderNativeMacLookAndFeel() && !Registry.is("ui.no.mac.scrollbar")
? NSScrollerHelper.getScrollerStyle()
: null;
if (style != myMacScrollerStyle && scrollbar != null) {
myMacScrollerStyle = style;
JScrollBar scrollBar = scrollbar;
uninstallUI(scrollBar);
installUI(scrollBar);
JScrollPane pane = JBScrollPane.findScrollPane(scrollBar);
if (pane != null) pane.revalidate();
}
}
public boolean alwaysShowTrack() {
return !isMacScrollbar();
return !isMacOverlayScrollbar();
}
@Override
@@ -192,7 +227,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
private void resetRegularThumbAnimator() {
if (isMacScrollbar()) return;
if (isMacOverlayScrollbar()) return;
myThumbFadeAnimator.reset();
if (scrollbar != null && scrollbar.getValueIsAdjusting() || myMouseIsOverThumb || Registry.is("ui.no.bangs.and.whistles")) {
@@ -205,7 +240,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
private void resetMacScrollbarExpandAnimator() {
if (!isMacScrollbar()) return;
if (!isMacOverlayScrollbar()) return;
if (myMouseOverScrollbarExpandLevel == 0) {
myMouseOverScrollbarExpandAnimator.reset();
@@ -217,7 +252,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
private void resetMacScrollbarFadeout() {
if (!isMacScrollbar()) return;
if (!isMacOverlayScrollbar()) return;
myMacScrollbarFadeAnimator.suspend();
myMacScrollbarFadeAnimator.reset();
@@ -246,6 +281,8 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
@Override
protected void installDefaults() {
myMacScrollerStyle = NSScrollerHelper.getScrollerStyle();
final int incGap = UIManager.getInt("ScrollBar.incrementButtonGap");
final int decGap = UIManager.getInt("ScrollBar.decrementButtonGap");
try {
@@ -270,22 +307,25 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
scrollbar.addMouseMotionListener(myMouseMotionListener);
scrollbar.addHierarchyListener(myHierarchyListener);
updateAWTMotionListener(false);
updateGlobalListeners(false);
resetRegularThumbAnimator();
resetMacScrollbarFadeout();
}
private void updateAWTMotionListener(boolean forceRemove) {
private void updateGlobalListeners(boolean forceRemove) {
boolean shouldAdd = scrollbar.isDisplayable();
if (myAWTMouseListenerAdded && (!shouldAdd || forceRemove)) {
if (myGlobalListenersAdded && (!shouldAdd || forceRemove)) {
Toolkit.getDefaultToolkit().removeAWTEventListener(myAWTMouseListener);
myAWTMouseListenerAdded = false;
NSScrollerHelper.removeScrollbarStyleListener(myNSScrollerListener);
myGlobalListenersAdded = false;
}
if (!myAWTMouseListenerAdded && shouldAdd && !forceRemove) {
if (!myGlobalListenersAdded && shouldAdd && !forceRemove) {
Toolkit.getDefaultToolkit().addAWTEventListener(myAWTMouseListener, AWTEvent.MOUSE_MOTION_EVENT_MASK);
myAWTMouseListenerAdded = true;
NSScrollerHelper.addScrollbarStyleListener(myNSScrollerListener);
myGlobalListenersAdded = true;
}
}
@@ -370,7 +410,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
scrollbar.removeMouseMotionListener(myMouseMotionListener);
scrollbar.removeHierarchyListener(myHierarchyListener);
updateAWTMotionListener(true);
updateGlobalListeners(true);
Disposer.dispose(myThumbFadeAnimator);
Disposer.dispose(myMouseOverScrollbarExpandAnimator);
@@ -384,7 +424,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
protected int getThickness() {
return isMacScrollbar() ? 15 : 13;
return isMacOverlayScrollbar() ? 15 : 13;
}
@Override
@@ -405,7 +445,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
@Override
public boolean contains(JComponent c, int x, int y) {
if (!alwaysShowTrack() && myMacScrollbarHidden) return false;
if (isMacOverlayScrollbar() && !alwaysShowTrack() && myMacScrollbarHidden) return false;
return super.contains(c, x, y);
}
@@ -418,7 +458,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
protected void doPaintTrack(Graphics g, JComponent c, Rectangle bounds) {
if (isMacScrollbar() && !alwaysShowTrack()) {
if (isMacOverlayScrollbar() && !alwaysShowTrack()) {
bounds = getMacScrollBarBounds(bounds, false);
boolean vertical = isVertical();
@@ -456,7 +496,6 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
@Override
protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
if (myMacScrollbarHidden) return;
doPaintThumb(g, thumbBounds);
}
@@ -465,7 +504,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
return;
}
if (isMacScrollbar()) {
if (isMacOverlayScrollbar()) {
paintMacThumb(g, thumbBounds);
}
else {
@@ -476,6 +515,8 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
private void paintMacThumb(Graphics g, Rectangle thumbBounds) {
if (myMacScrollbarHidden) return;
thumbBounds = getMacScrollBarBounds(thumbBounds, true);
Graphics2D g2d = (Graphics2D)g;
RenderingHints oldHints = g2d.getRenderingHints();
@@ -573,7 +614,7 @@ public class ButtonlessScrollBarUI extends BasicScrollBarUI {
}
protected Color adjustColor(Color c) {
if (isMacScrollbar()) {
if (isMacOverlayScrollbar()) {
int alpha = (int)((120 + (myMouseOverScrollbarExpandLevel * 20)) * (1 - myMacScrollbarFadeLevel));
return new Color(c.getRed(), c.getGreen(), c.getBlue(), alpha);
}
@@ -0,0 +1,116 @@
/*
* Copyright 2000-2014 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.intellij.util.ui;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.mac.foundation.Foundation;
import com.intellij.ui.mac.foundation.ID;
import com.sun.jna.Callback;
import com.sun.jna.Pointer;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.EventListenerList;
import java.util.EventListener;
import static com.intellij.ui.mac.foundation.Foundation.invoke;
class NSScrollerHelper {
private static final Callback CALLBACK = new Callback() {
@SuppressWarnings("UnusedDeclaration")
public void callback(ID self, Pointer selector, ID event) {
final Style style = getScrollerStyle();
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
fireStyleChanged(style);
}
});
}
};
public enum Style {Legacy, Overlay}
private static final EventListenerList ourListeners = new EventListenerList();
static {
if (SystemInfo.isMac) {
initNotificationObserver();
}
}
private static void initNotificationObserver() {
Foundation.NSAutoreleasePool pool = new Foundation.NSAutoreleasePool();
ID delegateClass = Foundation.allocateObjcClassPair(Foundation.getObjcClass("NSObject"), "NSScrollerChangesObserver");
if (!Foundation.addMethod(delegateClass, Foundation.createSelector("handleScrollerStyleChanged:"), CALLBACK, "v@")) {
throw new RuntimeException("Cannot add observer method");
}
;
Foundation.registerObjcClassPair(delegateClass);
ID delegate = invoke("NSScrollerChangesObserver", "new");
try {
ID defaultCenter = invoke("NSNotificationCenter", "defaultCenter");
invoke(defaultCenter, "addObserver:selector:name:object:",
delegate,
Foundation.createSelector("handleScrollerStyleChanged:"),
Foundation.nsString("NSPreferredScrollerStyleDidChangeNotification"),
ID.NIL);
}
finally {
pool.drain();
}
}
@NotNull
public static Style getScrollerStyle() {
if (!SystemInfo.isMac) return Style.Legacy;
Foundation.NSAutoreleasePool pool = new Foundation.NSAutoreleasePool();
try {
if (Foundation.invoke(Foundation.getObjcClass("NSScroller"), "preferredScrollerStyle").intValue() == 1) {
return Style.Overlay;
}
}
catch (Throwable ignore) {
}
finally {
pool.drain();
}
return Style.Legacy;
}
public static void addScrollbarStyleListener(@NotNull ScrollbarStyleListener listener) {
ourListeners.add(ScrollbarStyleListener.class, listener);
}
public static void removeScrollbarStyleListener(@NotNull ScrollbarStyleListener listener) {
ourListeners.remove(ScrollbarStyleListener.class, listener);
}
private static void fireStyleChanged(@NotNull Style style) {
Object[] listeners = ourListeners.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ScrollbarStyleListener.class) {
((ScrollbarStyleListener)listeners[i + 1]).styleChanged(style);
}
}
}
public interface ScrollbarStyleListener extends EventListener {
void styleChanged(@NotNull Style newStyle);
}
}
@@ -528,7 +528,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
return;
}
if (isMacScrollbar()) {
if (isMacOverlayScrollbar()) {
if (!isMirrored()) {
super.paintThumb(g, c, thumbBounds);
}
@@ -554,14 +554,14 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
@Override
protected int adjustThumbWidth(int width) {
if (isMacScrollbar() || UISettings.getInstance().PRESENTATION_MODE) return super.adjustThumbWidth(width);
if (isMacOverlayScrollbar() || UISettings.getInstance().PRESENTATION_MODE) return super.adjustThumbWidth(width);
return width - 2;
}
@Override
protected int getThickness() {
if (UISettings.getInstance().PRESENTATION_MODE) return super.getThickness();
return super.getThickness() + (isMacScrollbar() ? 2 : 7);
return super.getThickness() + (isMacOverlayScrollbar() ? 2 : 7);
}
@Override
@@ -618,7 +618,7 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark
@Override
protected Color adjustColor(Color c) {
if (isMacScrollbar()) return super.adjustColor(c);
if (isMacOverlayScrollbar()) return super.adjustColor(c);
if (UIUtil.isUnderDarcula()) {
return c;