WI-5951 apply html tag tree highlighting colors to breadcrumbs panel

This commit is contained in:
Eugene Kudelevsky
2011-04-20 16:25:35 +04:00
parent ec9f0ed988
commit 2b618185d1
13 changed files with 351 additions and 74 deletions
@@ -22,6 +22,8 @@
<extensionPoint name="breadcrumbsInfoProvider" interface="com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider"/>
<extensionPoint name="breadcrumbsPresentationProvider" interface="com.intellij.xml.breadcrumbs.BreadcrumbsPresentationProvider"/>
<extensionPoint name="xmlStructureViewElementProvider"
interface="com.intellij.ide.structureView.xml.XmlStructureViewElementProvider"/>
@@ -345,5 +347,7 @@
<xml.attributeDescriptorsProvider implementation="com.intellij.html.impl.Html5CustomAttributeDescriptorsProvider"/>
<sourceRootFinder implementation="com.intellij.ide.util.newProjectWizard.JavaSourceRootFinder"/>
<breadcrumbsPresentationProvider
implementation="com.intellij.codeInsight.daemon.impl.tagTreeHighlighting.HtmlTagTreeBreadcrumbsPresentationProvider"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2011 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.codeInsight.daemon.impl.tagTreeHighlighting;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.xml.breadcrumbs.BreadcrumbsPresentationProvider;
import com.intellij.xml.breadcrumbs.CrumbPresentation;
import com.intellij.xml.breadcrumbs.DefaultCrumbsPresentation;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
/**
* @author Eugene.Kudelevsky
*/
public class HtmlTagTreeBreadcrumbsPresentationProvider extends BreadcrumbsPresentationProvider {
private static boolean isMyContext(@NotNull PsiElement deepestElement) {
final PsiFile file = deepestElement.getContainingFile();
if (file == null || !HtmlTagTreeHighlightingUtil.isTagTreeHighlightingActive(file)) {
return false;
}
if (!HtmlTagTreeHighlightingUtil.containsParentTagsWithSameName(deepestElement)) {
return false;
}
return true;
}
@Override
public CrumbPresentation[] getCrumbPresentations(@NotNull PsiElement[] elements) {
if (elements.length == 0 || !isMyContext(elements[elements.length - 1])) {
return null;
}
final CrumbPresentation[] result = new CrumbPresentation[elements.length];
final Color[] baseColors = HtmlTagTreeHighlightingUtil.getBaseColors();
int index = 0;
for (int i = result.length - 1; i >= 0; i--) {
if (elements[i] instanceof XmlTag) {
result[i] = new MyCrumbPresentation(baseColors[index % baseColors.length]);
index++;
}
}
return result;
}
private static class MyCrumbPresentation extends DefaultCrumbsPresentation {
private final Color myColor;
private MyCrumbPresentation(Color color) {
myColor = color;
}
@Override
public Color getBackgroundColor(boolean selected, boolean hovered, boolean light) {
final Color baseColor = super.getBackgroundColor(selected, hovered, light);
return HtmlTagTreeHighlightingUtil.makeTransparent(myColor, baseColor, 0.1);
}
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.options.UnnamedConfigurable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.util.ui.UIUtil;
import com.intellij.xml.breadcrumbs.BreadcrumbsXmlWrapper;
import javax.swing.*;
import java.awt.event.ActionEvent;
@@ -91,6 +92,11 @@ public class HtmlTagTreeHighlightingConfigurable implements UnnamedConfigurable
if (fileEditor instanceof TextEditor) {
final Editor editor = ((TextEditor)fileEditor).getEditor();
HtmlTagTreeHighlightingPass.clearHighlightingAndLineMarkers(editor, project);
final BreadcrumbsXmlWrapper breadcrumbsXmlWrapper = BreadcrumbsXmlWrapper.getBreadcrumbsComponent(editor);
if (breadcrumbsXmlWrapper != null) {
breadcrumbsXmlWrapper.queueUpdate(editor);
}
}
}
}
@@ -26,9 +26,6 @@ import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.ColorKey;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.ex.MarkupModelEx;
@@ -44,14 +41,12 @@ import com.intellij.psi.xml.XmlChildRole;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
@@ -101,7 +96,7 @@ public class HtmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
return;
}
if (!containsParentTagsWithSameName(element)) {
if (!HtmlTagTreeHighlightingUtil.containsParentTagsWithSameName(element)) {
return;
}
@@ -113,22 +108,6 @@ public class HtmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
}
}
private static boolean containsParentTagsWithSameName(PsiElement element) {
final Set<String> names = new HashSet<String>();
while (element != null) {
if (element instanceof XmlTag) {
final String name = ((XmlTag)element).getName();
if (!names.add(name)) {
return true;
}
}
element = element.getParent();
}
return false;
}
@Nullable
private static Pair<TextRange, TextRange> getTagRanges(XmlTag tag) {
final ASTNode tagNode = tag.getNode();
@@ -187,7 +166,7 @@ public class HtmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
final List<HighlightInfo> highlightInfos = new ArrayList<HighlightInfo>(count * 2);
final MarkupModel markupModel = myEditor.getMarkupModel();
final Color[] baseColors = getBaseColors();
final Color[] baseColors = HtmlTagTreeHighlightingUtil.getBaseColors();
final Color[] colorsForEditor = toColorsForEditor(baseColors);
final Color[] colorsForLineMarkers = toColorsForLineMarkers(baseColors);
@@ -261,19 +240,6 @@ public class HtmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
}
private static Color[] getBaseColors() {
final ColorKey[] colorKeys = HtmlTagTreeHighlightingColors.getColorKeys();
final Color[] colors = new Color[colorKeys.length];
final EditorColorsScheme colorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
for (int i = 0; i < colors.length; i++) {
colors[i] = colorsScheme.getColor(colorKeys[i]);
}
return colors;
}
private static Color[] toColorsForLineMarkers(Color[] baseColors) {
final Color[] colors = new Color[baseColors.length];
final Color tagBackground = new Color(239, 239, 239);
@@ -298,28 +264,24 @@ public class HtmlTagTreeHighlightingPass extends TextEditorHighlightingPass {
}
private Color[] toColorsForEditor(Color[] baseColors) {
final Color[] colors = new Color[baseColors.length];
final Color tagBackground = myEditor instanceof EditorEx ? ((EditorEx)myEditor).getBackgroundColor() : null;
if (tagBackground == null) {
return baseColors;
}
final Color[] resultColors = new Color[baseColors.length];
// todo: make configurable
final double transparency = 0.1;
for (int i = 0; i < colors.length; i++) {
for (int i = 0; i < resultColors.length; i++) {
final Color color = baseColors[i];
if (tagBackground == null) {
colors[i] = color;
}
else {
int r = (int)(tagBackground.getRed() * (1 - transparency) + color.getRed() * transparency);
int g = (int)(tagBackground.getGreen() * (1 - transparency) + color.getGreen() * transparency);
int b = (int)(tagBackground.getBlue() * (1 - transparency) + color.getBlue() * transparency);
colors[i] = new Color(r, g, b);
}
final Color color1 = HtmlTagTreeHighlightingUtil.makeTransparent(color, tagBackground, transparency);
resultColors[i] = color1;
}
return colors;
return resultColors;
}
public static void clearHighlightingAndLineMarkers(final Editor editor, @NotNull Project project) {
@@ -15,18 +15,15 @@
*/
package com.intellij.codeInsight.daemon.impl.tagTreeHighlighting;
import com.intellij.application.options.editor.WebEditorOptions;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeHighlighting.TextEditorHighlightingPass;
import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory;
import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.AbstractProjectComponent;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.xml.util.HtmlUtil;
import org.jetbrains.annotations.NotNull;
/**
@@ -40,19 +37,9 @@ public class HtmlTagTreeHighlightingPassFactory extends AbstractProjectComponent
}
public TextEditorHighlightingPass createHighlightingPass(@NotNull final PsiFile file, @NotNull final Editor editor) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return null;
}
if (editor.isOneLineMode()) return null;
if (!(file instanceof XmlFile) || !HtmlUtil.hasHtml(file)) {
return null;
}
if (!WebEditorOptions.getInstance().isTagTreeHighlightingEnabled()) {
return null;
}
if (!HtmlTagTreeHighlightingUtil.isTagTreeHighlightingActive(file)) return null;
return new HtmlTagTreeHighlightingPass((XmlFile)file, editor);
}
@@ -0,0 +1,91 @@
/*
* Copyright 2000-2011 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.codeInsight.daemon.impl.tagTreeHighlighting;
import com.intellij.application.options.editor.WebEditorOptions;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.colors.ColorKey;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.containers.HashSet;
import com.intellij.xml.util.HtmlUtil;
import java.awt.*;
import java.util.Set;
/**
* @author Eugene.Kudelevsky
*/
class HtmlTagTreeHighlightingUtil {
private HtmlTagTreeHighlightingUtil() {
}
static boolean containsParentTagsWithSameName(PsiElement element) {
final Set<String> names = new HashSet<String>();
while (element != null) {
if (element instanceof XmlTag) {
final String name = ((XmlTag)element).getName();
if (!names.add(name)) {
return true;
}
}
element = element.getParent();
}
return false;
}
static boolean isTagTreeHighlightingActive(PsiFile file) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return false;
}
if (!(file instanceof XmlFile) || !HtmlUtil.hasHtml(file)) {
return false;
}
if (!WebEditorOptions.getInstance().isTagTreeHighlightingEnabled()) {
return false;
}
return true;
}
static Color makeTransparent(Color color, Color backgroundColor, double transparency) {
int r = (int)(backgroundColor.getRed() * (1 - transparency) + color.getRed() * transparency);
int g = (int)(backgroundColor.getGreen() * (1 - transparency) + color.getGreen() * transparency);
int b = (int)(backgroundColor.getBlue() * (1 - transparency) + color.getBlue() * transparency);
return new Color(r, g, b);
}
static Color[] getBaseColors() {
final ColorKey[] colorKeys = HtmlTagTreeHighlightingColors.getColorKeys();
final Color[] colors = new Color[colorKeys.length];
final EditorColorsScheme colorsScheme = EditorColorsManager.getInstance().getGlobalScheme();
for (int i = 0; i < colors.length; i++) {
colors[i] = colorsScheme.getColor(colorKeys[i]);
}
return colors;
}
}
@@ -24,12 +24,17 @@ import org.jetbrains.annotations.NotNull;
public class BreadcrumbsPsiItem extends BreadcrumbsItem {
private final PsiElement myElement;
private final BreadcrumbsInfoProvider myProvider;
private CrumbPresentation myPresentation;
public BreadcrumbsPsiItem(@NotNull final PsiElement element, @NotNull final BreadcrumbsInfoProvider provider) {
myElement = element;
myProvider = provider;
}
public void setPresentation(CrumbPresentation presentation) {
myPresentation = presentation;
}
public String getDisplayText() {
return isValid() ? myProvider.getElementInfo(myElement) : "INVALID";
}
@@ -39,6 +44,11 @@ public class BreadcrumbsPsiItem extends BreadcrumbsItem {
return s == null ? "" : s;
}
@Override
public CrumbPresentation getPresentation() {
return myPresentation;
}
public boolean isValid() {
return myElement != null && myElement.isValid();
}
@@ -30,6 +30,7 @@ import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vcs.FileStatusListener;
import com.intellij.openapi.vcs.FileStatusManager;
@@ -47,10 +48,8 @@ import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.*;
import java.util.List;
import java.util.PriorityQueue;
/**
* @author spleaner
@@ -65,8 +64,11 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
private final BreadcrumbsInfoProvider myInfoProvider;
private final JPanel myWrapperPanel;
public static final Key<BreadcrumbsXmlWrapper> BREADCRUMBS_COMPONENT_KEY = new Key<BreadcrumbsXmlWrapper>("BREADCRUMBS_KEY");
public BreadcrumbsXmlWrapper(@NotNull final Editor editor) {
myEditor = editor;
myEditor.putUserData(BREADCRUMBS_COMPONENT_KEY, this);
final Project project = editor.getProject();
assert project != null;
@@ -178,7 +180,7 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
myWrapperPanel.add(myComponent, BorderLayout.CENTER);
}
private void queueUpdate(Editor editor) {
public void queueUpdate(Editor editor) {
myQueue.cancelAllUpdates();
myQueue.queue(new MyUpdate(this, editor));
}
@@ -197,13 +199,36 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
return provider == null ? myInfoProvider : provider;
}
private static PsiElement[] toPsiElementArray(Collection<BreadcrumbsPsiItem> items) {
final PsiElement[] elements = new PsiElement[items.size()];
int index = 0;
for (BreadcrumbsPsiItem item : items) {
elements[index++] = item.getPsiElement();
}
return elements;
}
@Nullable
private static CrumbPresentation[] getCrumbPresentations(@NotNull final LinkedList<BreadcrumbsPsiItem> items,
@NotNull PsiElement firstElement) {
for (BreadcrumbsPresentationProvider provider : BreadcrumbsPresentationProvider.EP_NAME.getExtensions()) {
final PsiElement[] elements = toPsiElementArray(items);
final CrumbPresentation[] presentations = provider.getCrumbPresentations(elements);
if (presentations != null) {
return presentations;
}
}
return null;
}
private void setUserCaretChange(final boolean userCaretChange) {
myUserCaretChange = userCaretChange;
}
@Nullable
private List<BreadcrumbsPsiItem> getLineElements(@NotNull final LogicalPosition position) {
PsiElement element = findFirstBreadcrumbedElement(position);
final PsiElement firstElement = findFirstBreadcrumbedElement(position);
PsiElement element = firstElement;
if (element == null) return null;
final LinkedList<BreadcrumbsPsiItem> result = new LinkedList<BreadcrumbsPsiItem>();
@@ -217,6 +242,14 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
element = (provider != null) ? provider.getParent(element) : element.getParent();
}
final CrumbPresentation[] presentations = getCrumbPresentations(result, firstElement);
if (presentations != null) {
int i = 0;
for (BreadcrumbsPsiItem item : result) {
item.setPresentation(presentations[i++]);
}
}
return result;
}
@@ -302,7 +335,13 @@ public class BreadcrumbsXmlWrapper implements BreadcrumbsItemListener<Breadcrumb
}
}
@Nullable
public static BreadcrumbsXmlWrapper getBreadcrumbsComponent(@NotNull Editor editor) {
return editor.getUserData(BREADCRUMBS_COMPONENT_KEY);
}
public void dispose() {
myEditor.putUserData(BREADCRUMBS_COMPONENT_KEY, null);
myEditor = null;
}
@@ -588,7 +588,7 @@ public class BreadcrumbsComponent<T extends BreadcrumbsItem> extends JComponent
}
}
private static class ButtonSettings extends PainterSettings {
static class ButtonSettings extends PainterSettings {
protected static final Color DEFAULT_BG_COLOR = new Color(245, 245, 245);
private static final Color LIGHT_BG_COLOR = new Color(253, 253, 253);
private static final Color CURRENT_BG_COLOR = new Color(250, 250, 220);
@@ -599,23 +599,34 @@ public class BreadcrumbsComponent<T extends BreadcrumbsItem> extends JComponent
protected static final Color DEFAULT_BORDER_COLOR = new Color(90, 90, 90);
private static final Color LIGHT_BORDER_COLOR = new Color(170, 170, 170);
@Nullable
Color getBackgroundColor(@NotNull final Crumb c) {
if (c.isHovered()) {
static Color getBackgroundColor(boolean selected, boolean hovered, boolean light, boolean navigationCrumb) {
if (hovered) {
return HOVERED_BG_COLOR;
}
if (c.isSelected()) {
if (selected) {
return CURRENT_BG_COLOR;
}
if (c.isLight() && !(c instanceof NavigationCrumb)) {
if (light && !navigationCrumb) {
return LIGHT_BG_COLOR;
}
return DEFAULT_BG_COLOR;
}
@Nullable
Color getBackgroundColor(@NotNull final Crumb c) {
final BreadcrumbsItem item = c.getItem();
if (item != null) {
final CrumbPresentation presentation = item.getPresentation();
if (presentation != null) {
return presentation.getBackgroundColor(c.isSelected(), c.isHovered(), c.isLight());
}
}
return getBackgroundColor(c.isSelected(), c.isHovered(), c.isLight(), c instanceof NavigationCrumb);
}
@Nullable
Color getForegroundColor(@NotNull final Crumb c) {
if (c.isLight() && !c.isHovered() && !(c instanceof NavigationCrumb)) {
@@ -15,6 +15,8 @@
*/
package com.intellij.xml.breadcrumbs;
import org.jetbrains.annotations.Nullable;
/**
* @author spleaner
*/
@@ -26,4 +28,8 @@ public abstract class BreadcrumbsItem {
return "";
}
@Nullable
public CrumbPresentation getPresentation() {
return null;
}
}
@@ -0,0 +1,32 @@
/*
* Copyright 2000-2011 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.xml.breadcrumbs;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public abstract class BreadcrumbsPresentationProvider {
public static final ExtensionPointName<BreadcrumbsPresentationProvider> EP_NAME =
ExtensionPointName.create("com.intellij.breadcrumbsPresentationProvider");
@Nullable
public abstract CrumbPresentation[] getCrumbPresentations(@NotNull PsiElement[] element);
}
@@ -0,0 +1,25 @@
/*
* Copyright 2000-2011 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.xml.breadcrumbs;
import java.awt.*;
/**
* @author Eugene.Kudelevsky
*/
public abstract class CrumbPresentation {
public abstract Color getBackgroundColor(boolean selected, boolean hovered, boolean light);
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2011 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.xml.breadcrumbs;
import java.awt.*;
/**
* @author Eugene.Kudelevsky
*/
public class DefaultCrumbsPresentation extends CrumbPresentation {
@Override
public Color getBackgroundColor(boolean selected, boolean hovered, boolean light) {
return BreadcrumbsComponent.ButtonSettings.getBackgroundColor(selected, hovered, light, false);
}
}