IJPL-161142 vcs: fix CheckboxIcon rendering

The mutable 'mySelected' state does not work well with 'JBCachingScalableIcon' cache.

GitOrigin-RevId: 0610f54a55900be826137ebf119724d29daf6e0c
This commit is contained in:
Aleksey Pivovarov
2024-08-27 18:55:59 +02:00
committed by intellij-monorepo-bot
parent 52a3942a57
commit b9b03887d6
2 changed files with 33 additions and 30 deletions

View File

@@ -25,8 +25,18 @@ object CheckboxIcon {
fun createAndScale(color: Color): ColorIcon = JBUIScale.scaleIcon(create(color))
@JvmStatic
fun createAndScaleCheckbox(color: Color): WithColor {
return JBUIScale.scaleIcon(WithColor(iconSize, color, arcSize))
fun createAndScaleCheckbox(color: Color): JBScalableIcon {
return createAndScaleCheckbox(color, true)
}
@JvmStatic
fun createAndScaleCheckbox(color: Color, isSelected: Boolean): JBScalableIcon {
if (isSelected) {
return JBUIScale.scaleIcon(WithColor(iconSize, color, arcSize))
}
else {
return JBUIScale.scaleIcon(ColorIcon(iconSize, iconSize, iconSize, iconSize, color, false, arcSize))
}
}
private const val iconSize = 14
@@ -36,12 +46,9 @@ object CheckboxIcon {
get() = if (ExperimentalUI.isNewUI()) 4 else 0
class WithColor(size: Int, color: Color, arc: Int) : ColorIcon(size, size, size, size, color, false, arc) {
private var mySelected = false
class WithColor(private val size: Int, color: Color, arc: Int) : ColorIcon(size, size, size, size, color, false, arc) {
private var mySizedIcon: SizedIcon
fun isSelected() = mySelected
init {
val icon = if (ExperimentalUI.isNewUI()) {
IconUtil.resizeSquared(LafIconLookup.getIcon("checkmark", true), checkMarkSize)
@@ -52,10 +59,6 @@ object CheckboxIcon {
mySizedIcon = SizedIcon(icon, checkMarkSize, checkMarkSize)
}
fun prepare(selected: Boolean) {
mySelected = selected
}
override fun withIconPreScaled(preScaled: Boolean): WithColor {
mySizedIcon = mySizedIcon.withIconPreScaled(preScaled) as SizedIcon
return super.withIconPreScaled(preScaled) as WithColor
@@ -64,10 +67,12 @@ object CheckboxIcon {
override fun paintIcon(component: Component, g: Graphics, i: Int, j: Int) {
super.paintIcon(component, g, i, j)
if (mySelected) {
val offset = (iconWidth - mySizedIcon.iconWidth) / 2
mySizedIcon.paintIcon(component, g, i + offset, j + offset)
}
val offset = (iconWidth - mySizedIcon.iconWidth) / 2
mySizedIcon.paintIcon(component, g, i + offset, j + offset)
}
override fun copy(): ColorIcon {
return WithColor(size, iconColor, arcSize)
}
}
}

View File

@@ -43,9 +43,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.io.File;
import java.util.List;
import java.util.*;
import java.util.function.Function;
@@ -183,7 +185,7 @@ public class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFi
List<SelectVisibleRootAction> rootActions = new ArrayList<>();
if (myColorManager.hasMultiplePaths()) {
for (VirtualFile root : ContainerUtil.sorted(roots, FILE_BY_NAME_COMPARATOR)) {
rootActions.add(new SelectVisibleRootAction(root, rootActions));
rootActions.add(new SelectVisibleRootAction(root));
}
}
List<AnAction> structureActions = new ArrayList<>();
@@ -295,16 +297,19 @@ public class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFi
}
private final class SelectVisibleRootAction extends DumbAwareToggleAction {
final CheckboxIcon.WithColor myIcon;
final VirtualFile myRoot;
final List<SelectVisibleRootAction> myAllActions;
private final Icon mySelectedIcon;
private final Icon myDeselectedIcon;
SelectVisibleRootAction(@NotNull VirtualFile root, @NotNull List<SelectVisibleRootAction> allActions) {
final VirtualFile myRoot;
SelectVisibleRootAction(@NotNull VirtualFile root) {
super(null, root.getPresentableUrl(), null);
getTemplatePresentation().setText(root.getName(), false);
myRoot = root;
myAllActions = allActions;
myIcon = CheckboxIcon.createAndScaleCheckbox(myColorManager.getRootColor(myRoot));
Color color = myColorManager.getRootColor(myRoot);
mySelectedIcon = CheckboxIcon.createAndScaleCheckbox(color, true);
myDeselectedIcon = CheckboxIcon.createAndScaleCheckbox(color, false);
getTemplatePresentation().setIcon(JBUIScale.scaleIcon(EmptyIcon.create(CHECKBOX_ICON_SIZE))); // see PopupFactoryImpl.calcMaxIconSize
}
@@ -329,9 +334,6 @@ public class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFi
else {
setVisible(myRoot, state);
}
for (SelectVisibleRootAction action : myAllActions) {
action.updateIcon();
}
}
private static int getModifier() {
@@ -342,17 +344,13 @@ public class StructureFilterPopupComponent extends FilterPopupComponent<VcsLogFi
public void update(@NotNull AnActionEvent e) {
super.update(e);
updateIcon();
e.getPresentation().setIcon(myIcon);
boolean isSelected = isVisible(myRoot) && isEnabled();
e.getPresentation().setIcon(isSelected ? mySelectedIcon : myDeselectedIcon);
var modifierText = InputEvent.getModifiersExText(getModifier() << 6);
var tooltip = VcsLogBundle.message("vcs.log.filter.tooltip.click.to.see.only", modifierText, e.getPresentation().getText());
e.getPresentation().putClientProperty(ActionUtil.TOOLTIP_TEXT, tooltip);
}
private void updateIcon() {
myIcon.prepare(isVisible(myRoot) && isEnabled());
}
private boolean isEnabled() {
return getStructureFilter(myFilterModel.getFilter()) == null;
}