[platform] add TargetPresentation and its renderers (IDEA-198180)

This commit is contained in:
Daniil Ovchinnikov
2019-02-05 18:54:00 +03:00
parent 663ff9fae6
commit 21ff2883bc
4 changed files with 202 additions and 0 deletions
@@ -0,0 +1,59 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.navigation;
import com.intellij.openapi.editor.markup.TextAttributes;
import org.jetbrains.annotations.ApiStatus.Experimental;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* Represents presentation in target popup as follows:<br/>
* <code>| $icon $presentable_text (in $location_text) spacer $right_text $right_icon |</code><br/>
* Elements before spacer are aligned to the left, right text and right icon are aligned to the right.
*/
@Experimental
public interface TargetPresentation {
@Nullable
default Icon getIcon() {
return null;
}
@NotNull
String getPresentableText();
/**
* Note that returned instance's {@link TextAttributes#getBackgroundColor()} is used for the whole item background.
*
* @return attributes to highlight {@link #getPresentableText()}
*/
@Nullable
default TextAttributes getPresentableAttributes() {
return null;
}
@Nullable
default String getLocationText() {
return null;
}
/**
* @return attributes to highlight {@link #getLocationText()}
*/
@Nullable
default TextAttributes getLocationAttributes() {
return null;
}
@Nullable
default String getRightText() {
return null;
}
@Nullable
default Icon getRightIcon() {
return null;
}
}
@@ -0,0 +1,59 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.ui
import com.intellij.navigation.LocationPresentation.DEFAULT_LOCATION_PREFIX
import com.intellij.navigation.LocationPresentation.DEFAULT_LOCATION_SUFFIX
import com.intellij.navigation.TargetPresentation
import com.intellij.openapi.project.Project
import com.intellij.ui.ColoredListCellRenderer
import com.intellij.ui.JBColor
import com.intellij.ui.SimpleTextAttributes
import com.intellij.ui.SimpleTextAttributes.*
import com.intellij.ui.speedSearch.SearchAwareRenderer
import com.intellij.ui.speedSearch.SpeedSearchUtil.appendColoredFragmentForMatcher
import com.intellij.util.text.MatcherHolder
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.ApiStatus.Experimental
import javax.swing.JList
@Experimental
abstract class TargetPresentationMainRenderer<T>(
private val project: Project
) : ColoredListCellRenderer<T>(),
SearchAwareRenderer<T> {
protected abstract fun getPresentation(value: T): TargetPresentation?
final override fun getItemSearchString(item: T): String? = getPresentation(item)?.presentableText
final override fun customizeCellRenderer(list: JList<out T>, value: T, index: Int, selected: Boolean, hasFocus: Boolean) {
val presentation = getPresentation(value) ?: run {
append("Invalid", ERROR_ATTRIBUTES)
return
}
val attributes = presentation.presentableAttributes
val bgColor = attributes?.backgroundColor ?: UIUtil.getListBackground()
icon = presentation.icon
background = if (selected) UIUtil.getListSelectionBackground(hasFocus) else bgColor
val nameAttributes = attributes?.let(::fromTextAttributes)
?: SimpleTextAttributes(STYLE_PLAIN, list.foreground)
val matcher = MatcherHolder.getAssociatedMatcher(list)
appendColoredFragmentForMatcher(presentation.presentableText, this, nameAttributes, matcher, bgColor, selected)
presentation.locationText?.let { locationText ->
val locationAttributes = presentation.locationAttributes?.let {
merge(defaultLocationAttributes, fromTextAttributes(it))
} ?: defaultLocationAttributes
append(DEFAULT_LOCATION_PREFIX, defaultLocationAttributes)
append("in ", defaultLocationAttributes)
append(locationText, locationAttributes)
append(DEFAULT_LOCATION_SUFFIX, defaultLocationAttributes)
}
}
companion object {
private val defaultLocationAttributes = SimpleTextAttributes(STYLE_PLAIN, JBColor.GRAY)
}
}
@@ -0,0 +1,52 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.ui
import com.intellij.navigation.TargetPresentation
import com.intellij.ui.components.JBLabel
import com.intellij.ui.speedSearch.SearchAwareRenderer
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.ApiStatus.Experimental
import java.awt.Component
import javax.swing.JList
import javax.swing.ListCellRenderer
import javax.swing.SwingConstants
@Experimental
internal abstract class TargetPresentationRightRenderer<T> : ListCellRenderer<T>, SearchAwareRenderer<T> {
companion object {
private val ourBorder = JBUI.Borders.emptyRight(UIUtil.getListCellHPadding())
}
protected abstract fun getPresentation(value: T): TargetPresentation?
private val component = JBLabel().apply {
border = ourBorder
horizontalTextPosition = SwingConstants.LEFT
horizontalAlignment = SwingConstants.RIGHT // align icon to the right
}
final override fun getItemSearchString(item: T): String? = getPresentation(item)?.locationText
final override fun getListCellRendererComponent(list: JList<out T>,
value: T,
index: Int,
isSelected: Boolean,
cellHasFocus: Boolean): Component {
component.apply {
text = ""
icon = null
background = UIUtil.getListBackground(isSelected)
foreground = if (isSelected) UIUtil.getListSelectionForeground() else UIUtil.getInactiveTextColor()
font = list.font
}
getPresentation(value)?.let { presentation ->
presentation.rightText?.let { rightText ->
component.text = rightText
component.icon = presentation.rightIcon
}
}
return component
}
}
@@ -0,0 +1,32 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.ide.ui
import com.intellij.navigation.TargetPresentation
import com.intellij.openapi.project.Project
import com.intellij.ui.list.LeftRightSearchAwareRenderer
import com.intellij.ui.speedSearch.SearchAwareRenderer
import org.jetbrains.annotations.ApiStatus.Experimental
@Experimental
fun <T> createTargetPresentationRenderer(project: Project, presentation: (T) -> TargetPresentation?): SearchAwareRenderer<T> {
val mainRenderer = createMainRenderer(project, presentation)
if (UISettings.instance.showIconInQuickNavigation) {
val rightRenderer = createRightRenderer(presentation)
return LeftRightSearchAwareRenderer(mainRenderer, rightRenderer)
}
else {
return mainRenderer
}
}
private fun <T> createMainRenderer(project: Project, presentation: (T) -> TargetPresentation?): SearchAwareRenderer<T> {
return object : TargetPresentationMainRenderer<T>(project) {
override fun getPresentation(value: T): TargetPresentation? = presentation(value)
}
}
private fun <T> createRightRenderer(presentation: (T) -> TargetPresentation?): SearchAwareRenderer<T> {
return object : TargetPresentationRightRenderer<T>() {
override fun getPresentation(value: T): TargetPresentation? = presentation(value)
}
}