mac sheets emulation

This commit is contained in:
Vassiliy
2013-06-27 17:08:55 +04:00
parent 0ebe9d6cf6
commit ec0ce0adf3
5 changed files with 196 additions and 29 deletions
@@ -100,6 +100,8 @@ public abstract class DialogWrapper {
@NonNls public static final String FOCUSED_ACTION = "FocusedAction";
@NonNls private static final String NO_AUTORESIZE = "NoAutoResizeAndFit";
private static final KeyStroke SHOW_OPTION_KEYSTROKE = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
InputEvent.ALT_MASK | InputEvent.SHIFT_MASK);
@@ -771,6 +773,18 @@ public abstract class DialogWrapper {
myPeer.toBack();
}
protected boolean setAutoAdjustable(boolean autoAdjustable) {
JRootPane rootPane = getRootPane();
if (rootPane == null) return false;
rootPane.putClientProperty(NO_AUTORESIZE, autoAdjustable? null : Boolean.TRUE);
return true;
}
//true by default
public boolean isAutoAdjustable() {
JRootPane rootPane = getRootPane();
return rootPane == null || rootPane.getClientProperty(NO_AUTORESIZE) == null;
}
/**
* Dispose the wrapped and releases all resources allocated be the wrapper to help
* more effecient garbage collection. You should never invoke this method twice or
@@ -793,7 +807,7 @@ public abstract class DialogWrapper {
// if rootPane = null, dialog has already been disposed
if (rootPane != null) {
unregisterKeyboardActions(rootPane);
if (myActualSize != null) {
if (myActualSize != null && isAutoAdjustable()) {
setSize(myActualSize.width, myActualSize.height);
}
myPeer.dispose();
@@ -1102,7 +1116,7 @@ public abstract class DialogWrapper {
myErrorText = new ErrorText();
myErrorText.setVisible(false);
final JPanel root = new JPanel(new BorderLayout());
final JPanel root = new JPanel(createRootLayout());
//{
// @Override
// public void paint(Graphics g) {
@@ -1168,6 +1182,10 @@ public abstract class DialogWrapper {
}
}
LayoutManager createRootLayout() {
return new BorderLayout();
}
private static void installEnterHook(JComponent root) {
new AnAction() {
@Override
@@ -18,17 +18,21 @@ package com.intellij.openapi.ui;
import com.intellij.CommonBundle;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.ui.*;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.mac.MacMessages;
import com.intellij.util.Alarm;
import com.intellij.util.Function;
import com.intellij.util.PairFunction;
import com.intellij.util.execution.ParametersListUtil;
@@ -44,8 +48,10 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Messages {
public static final int OK = 0;
@@ -138,6 +144,10 @@ public class Messages {
&& !DialogWrapper.isMultipleModalDialogs();
}
public static boolean isMacSheetEmulation() {
return SystemInfo.isMac && !Registry.is("ide.mac.message.dialogs.as.sheets");
}
public static int showDialog(Project project, String message, String title, String moreInfo, String[] options, int defaultOptionIndex, int focusedOptionIndex, Icon icon) {
if (isApplicationInUnitTestOrHeadless()) {
return ourTestImplementation.show(message);
@@ -944,6 +954,7 @@ public class Messages {
protected int myDefaultOptionIndex;
protected int myFocusedOptionIndex;
protected Icon myIcon;
private MyBorderLayout myLayout;
public MessageDialog(@Nullable Project project, String message, String title, String[] options, int defaultOptionIndex, @Nullable Icon icon, boolean canBeParent) {
this(project, message, title, options, defaultOptionIndex, -1, icon, canBeParent);
@@ -1003,6 +1014,9 @@ public class Messages {
protected void _init(String title, String message, String[] options, int defaultOptionIndex, int focusedOptionIndex, @Nullable Icon icon, @Nullable DoNotAskOption doNotAskOption) {
setTitle(title);
if (isMacSheetEmulation()) {
setUndecorated(true);
}
myMessage = message;
myOptions = options;
myDefaultOptionIndex = defaultOptionIndex;
@@ -1061,6 +1075,90 @@ public class Messages {
return doCreateCenterPanel();
}
@Override
LayoutManager createRootLayout() {
return isMacSheetEmulation() ? myLayout = new MyBorderLayout() : super.createRootLayout();
}
@Override
protected void dispose() {
if (isMacSheetEmulation()) {
animate();
} else {
super.dispose();
}
}
@Override
public void show() {
if (isMacSheetEmulation()) {
setInitialLocationCallback(new Computable<Point>() {
@Override
public Point compute() {
JRootPane rootPane = SwingUtilities.getRootPane(getWindow().getParent());
if (rootPane == null) {
rootPane = SwingUtilities.getRootPane(getWindow().getOwner());
}
Point p = rootPane.getLocationOnScreen();
p.x += (rootPane.getWidth() - getWindow().getWidth()) / 2;
return p;
}
});
animate();
if (SystemInfo.isJavaVersionAtLeast("1.7")) {
try {
Method method = Class.forName("java.awt.Window").getDeclaredMethod("setOpacity", float.class);
if (method != null) method.invoke(getPeer().getWindow(), .8f);
}
catch (Exception ignored) {
}
}
setAutoAdjustable(false);
setSize(getPreferredSize().width, 0);//initial state before animation, zero height
}
super.show();
}
private void animate() {
final int height = getPreferredSize().height;
final int frameCount = 10;
final boolean toClose = isShowing();
final AtomicInteger i = new AtomicInteger(-1);
final Alarm animator = new Alarm(myDisposable);
final Runnable runnable = new Runnable() {
@Override
public void run() {
int state = i.addAndGet(1);
double linearProgress = (double)state / frameCount;
if (toClose) {
linearProgress = 1 - linearProgress;
}
myLayout.myPhase = (1 - Math.cos(Math.PI * linearProgress)) / 2;
Window window = getPeer().getWindow();
Rectangle bounds = window.getBounds();
bounds.height = (int)(height * myLayout.myPhase);
window.setBounds(bounds);
if (state == 0 && !toClose && window.getOwner() instanceof IdeFrame) {
WindowManager.getInstance().requestUserAttention((IdeFrame)window.getOwner(), true);
}
if (state < frameCount) {
animator.addRequest(this, 10);
}
else if (toClose) {
MessageDialog.super.dispose();
}
}
};
animator.addRequest(runnable, 10, ModalityState.stateForComponent(getRootPane()));
}
protected JComponent doCreateCenterPanel() {
JPanel panel = new JPanel(new BorderLayout(15, 0));
if (myIcon != null) {
@@ -1105,6 +1203,33 @@ public class Messages {
}
}
private static class MyBorderLayout extends BorderLayout {
private double myPhase = 0;//it varies from 0 (hidden state) to 1 (fully visible)
private MyBorderLayout() {
}
@Override
public void layoutContainer(Container target) {
final Dimension realSize = target.getSize();
target.setSize(target.getPreferredSize());
super.layoutContainer(target);
target.setSize(realSize);
synchronized (target.getTreeLock()) {
int yShift = (int)((1 - myPhase) * target.getPreferredSize().height);
Component[] components = target.getComponents();
for (Component component : components) {
Point point = component.getLocation();
point.y -= yShift;
component.setLocation(point);
}
}
}
}
public static void installHyperlinkSupport(JTextPane messageComponent) {
configureMessagePaneUi(messageComponent, "<html></html>");
}
@@ -127,4 +127,6 @@ public abstract class WindowManager {
* @return <code>true</code> if full screen mode is supported in current OS.
*/
public abstract boolean isFullScreenSupportedInCurrentOS();
public abstract void requestUserAttention(@NotNull IdeFrame frame, boolean critical);
}
@@ -112,6 +112,15 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra
window = focusedWindow;
}
}
if (window == null) {
IdeFrame[] frames = myWindowManager.getAllProjectFrames();
for (IdeFrame frame : frames) {
if (frame instanceof IdeFrameImpl && ((IdeFrameImpl)frame).isActive()) {
window = (IdeFrameImpl)frame;
break;
}
}
}
}
Window owner;
@@ -648,32 +657,34 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra
myFocusTrackback = new FocusTrackback(getDialogWrapper(), getParent(), true);
final DialogWrapper dialogWrapper = getDialogWrapper();
pack();
Dimension packedSize = getSize();
Dimension minSize = getMinimumSize();
setSize(Math.max(packedSize.width, minSize.width), Math.max(packedSize.height, minSize.height));
setSize((int)(getWidth() * dialogWrapper.getHorizontalStretch()), (int)(getHeight() * dialogWrapper.getVerticalStretch()));
// Restore dialog's size and location
myDimensionServiceKey = dialogWrapper.getDimensionKey();
boolean isAutoAdjustable = dialogWrapper.isAutoAdjustable();
Point location = null;
if (isAutoAdjustable) {
pack();
if (myDimensionServiceKey != null) {
final Project projectGuess = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(this));
location = DimensionService.getInstance().getLocation(myDimensionServiceKey, projectGuess);
Dimension size = DimensionService.getInstance().getSize(myDimensionServiceKey, projectGuess);
if (size != null) {
myInitialSize = new Dimension(size);
_setSizeForLocation(myInitialSize.width, myInitialSize.height, location);
Dimension packedSize = getSize();
Dimension minSize = getMinimumSize();
setSize(Math.max(packedSize.width, minSize.width), Math.max(packedSize.height, minSize.height));
setSize((int)(getWidth() * dialogWrapper.getHorizontalStretch()), (int)(getHeight() * dialogWrapper.getVerticalStretch()));
// Restore dialog's size and location
myDimensionServiceKey = dialogWrapper.getDimensionKey();
if (myDimensionServiceKey != null) {
final Project projectGuess = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(this));
location = DimensionService.getInstance().getLocation(myDimensionServiceKey, projectGuess);
Dimension size = DimensionService.getInstance().getSize(myDimensionServiceKey, projectGuess);
if (size != null) {
myInitialSize = new Dimension(size);
_setSizeForLocation(myInitialSize.width, myInitialSize.height, location);
}
}
}
if (myInitialSize == null) {
myInitialSize = getSize();
if (myInitialSize == null) {
myInitialSize = getSize();
}
}
if (location == null) {
@@ -687,10 +698,11 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra
setLocationRelativeTo(getOwner());
}
final Rectangle bounds = getBounds();
ScreenUtil.fitToScreen(bounds);
setBounds(bounds);
if (isAutoAdjustable) {
final Rectangle bounds = getBounds();
ScreenUtil.fitToScreen(bounds);
setBounds(bounds);
}
addWindowListener(new WindowAdapter() {
@Override
public void windowActivated(WindowEvent e) {
@@ -1052,7 +1064,9 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra
@Override
@SuppressWarnings({"RefusedBequest"})
public void componentResized(ComponentEvent e) {
UIUtil.adjustWindowToMinimumSize(getWindow());
if (getDialogWrapper().isAutoAdjustable()) {
UIUtil.adjustWindowToMinimumSize(getWindow());
}
}
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.impl.CommandProcessor;
import com.intellij.openapi.wm.impl.DesktopLayout;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.ui.AppIcon;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,6 +42,13 @@ public abstract class WindowManagerEx extends WindowManager {
public abstract IdeFrameImpl getFrame(@Nullable Project project);
@Override
public void requestUserAttention(@NotNull IdeFrame frame, boolean critical) {
Project project = frame.getProject();
if (project != null)
AppIcon.getInstance().requestAttention(project, critical);
}
public abstract IdeFrameImpl allocateFrame(Project project);
public abstract void releaseFrame(IdeFrameImpl frame);