diff --git a/platform/platform-api/platform-api.iml b/platform/platform-api/platform-api.iml
index 3a4898f7c5cd..127cfb114b38 100644
--- a/platform/platform-api/platform-api.iml
+++ b/platform/platform-api/platform-api.iml
@@ -36,6 +36,7 @@
+
diff --git a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java
index 72c85333b773..1754f711c7af 100644
--- a/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java
+++ b/platform/platform-api/src/com/intellij/ui/components/JBScrollPane.java
@@ -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();
diff --git a/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java b/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java
index 6f79570d614c..384ad56dd92f 100644
--- a/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java
+++ b/platform/platform-api/src/com/intellij/util/ui/ButtonlessScrollBarUI.java
@@ -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);
}
diff --git a/platform/platform-api/src/com/intellij/util/ui/NSScrollerHelper.java b/platform/platform-api/src/com/intellij/util/ui/NSScrollerHelper.java
new file mode 100644
index 000000000000..5040073673f1
--- /dev/null
+++ b/platform/platform-api/src/com/intellij/util/ui/NSScrollerHelper.java
@@ -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);
+ }
+}
diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
index 333431dd5f1c..65f8ed660c18 100644
--- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
+++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java
@@ -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;