wide selection in trees under aqua laf + deferred icon tree cache invalidation fix...
authorAlexey Pegov <alexey.pegov@jetbrains.com>
Tue, 27 Apr 2010 12:42:01 +0000 (16:42 +0400)
committerAlexey Pegov <alexey.pegov@jetbrains.com>
Tue, 27 Apr 2010 12:42:01 +0000 (16:42 +0400)
platform/icons/src/mac/tree_white_down_arrow.png [new file with mode: 0644]
platform/icons/src/mac/tree_white_right_arrow.png [new file with mode: 0644]
platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewTree.java
platform/lang-impl/src/com/intellij/ui/DeferredIconImpl.java
platform/platform-api/src/com/intellij/ui/ColoredTreeCellRenderer.java
platform/platform-api/src/com/intellij/ui/HighlightableCellRenderer.java
platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java
platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java
platform/platform-impl/src/com/intellij/ui/mac/foundation/Foundation.java
platform/util/src/com/intellij/util/ui/UIUtil.java

diff --git a/platform/icons/src/mac/tree_white_down_arrow.png b/platform/icons/src/mac/tree_white_down_arrow.png
new file mode 100644 (file)
index 0000000..05e6a25
Binary files /dev/null and b/platform/icons/src/mac/tree_white_down_arrow.png differ
diff --git a/platform/icons/src/mac/tree_white_right_arrow.png b/platform/icons/src/mac/tree_white_right_arrow.png
new file mode 100644 (file)
index 0000000..bda0b19
Binary files /dev/null and b/platform/icons/src/mac/tree_white_right_arrow.png differ
index 8c27ec0529b3665ac7105ae8a065b13d29b3f423..584418918dd89d6e0458cc204a705157ec5a6ae3 100644 (file)
@@ -19,10 +19,7 @@ package com.intellij.ide.projectView.impl;
 import com.intellij.ide.dnd.aware.DnDAwareTree;
 import com.intellij.ide.util.treeView.NodeDescriptor;
 import com.intellij.ide.util.treeView.NodeRenderer;
-import com.intellij.openapi.util.SystemInfo;
-import com.intellij.util.ui.UIUtil;
 
-import javax.swing.plaf.TreeUI;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.TreeModel;
 import javax.swing.tree.TreePath;
@@ -37,24 +34,10 @@ public abstract class ProjectViewTree extends DnDAwareTree {
   protected ProjectViewTree(TreeModel newModel) {
     super(newModel);
 
-    if (SystemInfo.isMac) setUI(new UIUtil.LeglessTreeUi());
-
     final NodeRenderer renderer = new NodeRenderer();
     renderer.setOpaque(false);
     renderer.setIconOpaque(false);
     setCellRenderer(renderer);
-
-    //setOpaque(false);
-  }
-
-  @Override
-  public void setUI(final TreeUI ui) {
-    TreeUI actualUI = ui;
-    if (SystemInfo.isMac && !(ui instanceof UIUtil.LeglessTreeUi)) {
-      actualUI = new UIUtil.LeglessTreeUi();
-    }
-
-    super.setUI(actualUI);
   }
 
   @Override
index a11affc7279c436415d04a6e2aa74bf3b02f6091..53fa5b1932d6a6c7db9333187fccb1e0d49dc5ac 100644 (file)
@@ -106,11 +106,8 @@ public class DeferredIconImpl<T> implements DeferredIcon {
                 if (target instanceof JTree) {
                   final TreeUI ui = ((JTree)target).getUI();
                   if (ui instanceof BasicTreeUI) {
-                    // yep, reset size cache
-                    int indent = ((Integer)UIManager.get("Tree.leftChildIndent")).intValue();
-                    if (((BasicTreeUI)ui).getLeftChildIndent() != indent) {
-                      ((BasicTreeUI)ui).setLeftChildIndent(indent);
-                    }
+                    // this call is "fake" and only need to reset tree layout cache
+                    ((BasicTreeUI)ui).setLeftChildIndent(((Integer)UIManager.get("Tree.leftChildIndent")).intValue());
                   }
                 }
               }
index 8bc4da4f20d405f6024bcdcc7fa8f372e62122af..6d5cda5f6ba9a40254867f7f64aefbb49fe01e01 100644 (file)
@@ -63,6 +63,10 @@ public abstract class ColoredTreeCellRenderer extends SimpleColoredComponent imp
     if (UIUtil.isFullRowSelectionLAF()) {
         setBackground(selected ? UIUtil.getTreeSelectionBackground() : null);
     }
+    else if (UIUtil.isUnderAquaLookAndFeel() && tree.getUI() instanceof UIUtil.MacTreeUI) {
+      setPaintFocusBorder(false);
+      setBackground(null);
+    }
     else {
       if (selected) {
         setPaintFocusBorder(true);
@@ -91,6 +95,10 @@ public abstract class ColoredTreeCellRenderer extends SimpleColoredComponent imp
       super.setOpaque(false);  // avoid erasing Nimbus focus frame
       super.setIconOpaque(false);
     }
+    else if (UIUtil.isUnderAquaLookAndFeel() && tree.getUI() instanceof UIUtil.MacTreeUI) {
+      super.setOpaque(false);  // avoid erasing Nimbus focus frame
+      super.setIconOpaque(false);
+    }
     else {
       super.setOpaque(myOpaque || selected && hasFocus || selected && tree.hasFocus()); // draw selection background even for non-opaque tree
     }
index 34a2fe4dbc4413498a9d81348af0a0fcc56a2583..ee635e3f0c61a9aa6bf43081353ba79779881411 100644 (file)
@@ -35,7 +35,16 @@ public class HighlightableCellRenderer extends HighlightableComponent implements
     setFont(UIUtil.getTreeFont());
     setIcon(null);
 
-    myIsSelected = selected;
+    if (tree.getUI() instanceof UIUtil.MacTreeUI) {
+      setOpaque(false);
+      myIsSelected = false;
+      myHasFocus = false;
+    } else {
+      setOpaque(true);
+      myIsSelected = selected;
+      myHasFocus = hasFocus;
+    }
+    
     myHasFocus = hasFocus;
     return this;
   }
index cc20898b9ef956fd6b37b8dcf225a8ebb8841933..fbd40f0cd46cc4d43453a7ad7d001b87afcdba0d 100644 (file)
@@ -80,6 +80,20 @@ public class Tree extends JTree implements ComponentWithEmptyText, Autoscroll, Q
     setCellRenderer(new NodeRenderer());
   }
 
+  @Override
+  public void setUI(final TreeUI ui) {
+    TreeUI actualUI = ui;
+    if (SystemInfo.isMac && !isCustomUI() && UIUtil.isUnderAquaLookAndFeel() && !(ui instanceof UIUtil.MacTreeUI)) {
+      actualUI = new UIUtil.MacTreeUI();
+    }
+
+    super.setUI(actualUI);
+  }
+
+  protected boolean isCustomUI() {
+    return false;
+  }
+
   public String getEmptyText() {
     return myEmptyTextHelper.getEmptyText();
   }
index ed3134b3fc2d870b2ba13b0c899d1274460ccdda..942d07f5a5e7c15f1b176d40aa2b4d3671a9e217 100644 (file)
@@ -601,6 +601,11 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl
       super.setUI(actualUI);
     }
 
+    @Override
+    protected boolean isCustomUI() {
+      return true;
+    }
+
     @Override
     protected void configureUiHelper(final TreeUIHelper helper) {
       helper.installToolTipHandler(this);
index 750628620ca0c8ece265c54d0835fd63d3181fdd..104f9ca5f0a10a2bf34c30e0c5165806fe98bc51 100644 (file)
@@ -61,6 +61,10 @@ public class Foundation {
     return myFoundationLibrary.objc_msgSend(id, selector, args);
   }
 
+  public static ID invoke(final String cls, final String selector, Object... args) {
+    return invoke(getClass(cls), createSelector(selector), args);
+  }
+
   public static ID registerObjcClass(ID superCls, String name) {
     return myFoundationLibrary.objc_allocateClassPair(superCls, name, 0);
   }
index 84fdb70b15815e87f3d9038438a3d8993ec66445..7d14eb2152e6ecca9c36abf6498cdf35dc3fcfaf 100644 (file)
@@ -31,13 +31,15 @@ import org.jetbrains.annotations.Nullable;
 import org.jetbrains.annotations.TestOnly;
 
 import javax.swing.*;
-import javax.swing.text.html.HTMLEditorKit;
-import javax.swing.text.html.StyleSheet;
 import javax.swing.border.Border;
 import javax.swing.border.LineBorder;
 import javax.swing.plaf.ProgressBarUI;
 import javax.swing.plaf.basic.BasicTreeUI;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.StyleSheet;
+import javax.swing.tree.AbstractLayoutCache;
 import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
 import java.awt.*;
 import java.awt.event.*;
 import java.lang.reflect.Field;
@@ -1034,7 +1036,34 @@ public class UIUtil {
     c.putClientProperty(FOCUS_PROXY_KEY, isProxy ? Boolean.TRUE : null);
   }
 
-  public static class LeglessTreeUi extends BasicTreeUI {
+  public static class MacTreeUI extends BasicTreeUI {
+
+    private static final Color SELECTION_COLOR = new Color(115, 132, 153);
+    private static final Color UNFOCUSED_SELECTION_COLOR = new Color(212, 212, 212);
+
+    private static final Icon TREE_COLLAPSED_ICON = (Icon) UIManager.get("Tree.collapsedIcon");
+    private static final Icon TREE_EXPANDED_ICON = (Icon) UIManager.get("Tree.expandedIcon");
+    private static final Icon TREE_SELECTED_COLLAPSED_ICON = IconLoader.getIcon("/mac/tree_white_right_arrow.png");
+    private static final Icon TREE_SELECTED_EXPANDED_ICON = IconLoader.getIcon("/mac/tree_white_down_arrow.png");
+
+    @Override
+    protected void completeUIInstall() {
+      super.completeUIInstall();
+
+      tree.setOpaque(false);
+      tree.setLargeModel(true);
+      tree.setRootVisible(false);
+      tree.setShowsRootHandles(true);
+    }
+
+    @Override
+    protected void installKeyboardActions() {
+      super.installKeyboardActions();
+
+      tree.getInputMap().put(KeyStroke.getKeyStroke("pressed LEFT"), "collapse");
+      tree.getInputMap().put(KeyStroke.getKeyStroke("pressed RIGHT"), "expand");
+    }
+
     @Override
     protected void paintHorizontalPartOfLeg(final Graphics g,
                                             final Rectangle clipBounds,
@@ -1056,6 +1085,84 @@ public class UIUtil {
     @Override
     protected void paintVerticalPartOfLeg(final Graphics g, final Rectangle clipBounds, final Insets insets, final TreePath path) {
     }
+
+    @Override
+    protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left, int right) {
+    }
+
+    @Override
+    public void paint(Graphics g, JComponent c) {
+      final TreeSelectionModel selectionModel = getSelectionModel();
+      final int[] selectedRows = selectionModel.getSelectionRows();
+      if (selectedRows != null) {
+        for (final int row : selectedRows) {
+          Rectangle bounds = tree.getRowBounds(row);
+          Graphics2D selectionBackgroundGraphics = (Graphics2D)g.create();
+          selectionBackgroundGraphics.translate(0, bounds.y);
+
+          selectionBackgroundGraphics.setColor(tree.hasFocus() ? SELECTION_COLOR : UNFOCUSED_SELECTION_COLOR);
+          selectionBackgroundGraphics.fillRect(0, 0, c.getWidth(), bounds.height - 1);
+          selectionBackgroundGraphics.dispose();
+        }
+      }
+
+      super.paint(g, c);
+    }
+
+    @Override
+    protected void paintExpandControl(Graphics g,
+                                      Rectangle clipBounds,
+                                      Insets insets,
+                                      Rectangle bounds,
+                                      TreePath path,
+                                      int row,
+                                      boolean isExpanded,
+                                      boolean hasBeenExpanded,
+                                      boolean isLeaf) {
+      boolean isPathSelected = tree.getSelectionModel().isPathSelected(path);
+
+      Icon expandIcon = isPathSelected && tree.hasFocus() ? TREE_SELECTED_EXPANDED_ICON
+                                       : TREE_EXPANDED_ICON;
+      Icon collapseIcon = isPathSelected && tree.hasFocus() ? TREE_SELECTED_COLLAPSED_ICON
+                                         : TREE_COLLAPSED_ICON;
+
+
+      if (!isLeaf(row)) {
+        setExpandedIcon(expandIcon);
+        setCollapsedIcon(collapseIcon);
+      }
+
+      super.paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf);
+    }
+
+    @Override
+    protected AbstractLayoutCache.NodeDimensions createNodeDimensions() {
+      return new NodeDimensionsHandler() {
+        @Override
+        public Rectangle getNodeDimensions(
+          Object value, int row, int depth, boolean expanded, Rectangle size) {
+          Rectangle dimensions = super.getNodeDimensions(value, row, depth, expanded, size);
+          int containerWidth = tree.getParent() instanceof JViewport
+                               ? tree.getParent().getWidth() : tree.getWidth();
+
+          if (containerWidth > 0) {
+            dimensions.width = containerWidth - getRowX(row, depth);
+          }
+
+          return dimensions;
+        }
+      };
+    }
+
+    @Override
+    public Rectangle getPathBounds(JTree tree, TreePath path) {
+        Rectangle bounds = super.getPathBounds(tree, path);
+        if (bounds != null) {
+            bounds.x = 0;
+            bounds.width = tree.getWidth();
+        }
+        return bounds;
+    }
   }
 
   public static boolean isPrinting(Graphics g) {