IDEA-354490 floating action fixes

GitOrigin-RevId: 16f9b4b6fbebc22793bd98cf87152e651adbfc8c
This commit is contained in:
Anton Kozub
2024-10-09 13:58:50 +02:00
committed by intellij-monorepo-bot
parent 76fbc266dc
commit 20c62c2d7e
6 changed files with 124 additions and 71 deletions

View File

@@ -0,0 +1,38 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.openapi.actionSystem
import com.intellij.icons.AllIcons
import org.jetbrains.annotations.ApiStatus
/**
* Actions which can behave as a group if they > 1 or as one action if action is only one
*/
@ApiStatus.Internal
class MergeableActions(
private val originalGroup: ActionGroup,
): ActionGroup() {
override fun getActionUpdateThread(): ActionUpdateThread {
return ActionUpdateThread.BGT
}
override fun update(e: AnActionEvent) {
val children = originalGroup.getChildren(e)
var visibleChildrenCount = 0
for (child in children) {
child.update(e)
if (e.presentation.isVisible) {
visibleChildrenCount++
if (visibleChildrenCount > 1) break
}
}
e.presentation.isPopupGroup = visibleChildrenCount > 1
e.presentation.isEnabledAndVisible = visibleChildrenCount > 0
e.presentation.icon = AllIcons.Actions.More
}
override fun getChildren(e: AnActionEvent?): Array<AnAction> {
return originalGroup.getChildren(e)
}
}

View File

@@ -9,6 +9,7 @@ import com.intellij.ide.structureView.customRegions.CustomRegionTreeElement;
import com.intellij.ide.structureView.impl.StructureViewFactoryImpl;
import com.intellij.ide.structureView.impl.common.PsiTreeElementBase;
import com.intellij.ide.structureView.logical.LogicalStructureDataKeys;
import com.intellij.ide.structureView.logical.impl.LogicalStructureViewModel;
import com.intellij.ide.structureView.logical.impl.LogicalStructureViewTreeElement;
import com.intellij.ide.structureView.symbol.DelegatingPsiElementWithSymbolPointer;
import com.intellij.ide.ui.UISettingsListener;
@@ -24,7 +25,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.toolbar.floating.FloatingToolbar;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.TextEditor;
@@ -79,8 +79,6 @@ import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.*;
@@ -124,7 +122,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
// written from EDT only
private volatile @Nullable CancellablePromise<?> myLastAutoscrollPromise;
private final FloatingToolbar floatingToolbar;
private StructureViewFloatingToolbar floatingToolbar;
public StructureViewComponent(@Nullable FileEditor editor,
@@ -183,22 +181,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
});
JScrollPane content = ScrollPaneFactory.createScrollPane(myTree);
MyLayeredPane layeredPane = new MyLayeredPane(content);
floatingToolbar = new FloatingToolbar(
layeredPane,
(ActionGroup) ActionManager.getInstance().getAction(ActionPlaces.STRUCTURE_VIEW_FLOATING_TOOLBAR),
this
);
layeredPane.add(content, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(floatingToolbar, JLayeredPane.POPUP_LAYER);
setContent(layeredPane);
content.getVerticalScrollBar().addAdjustmentListener(event -> floatingToolbar.setScrollingDy(event.getValue()));
addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
floatingToolbar.setX(getBounds().width - 50);
}
});
setContent(new MyLayeredPane(content, structureViewModel instanceof LogicalStructureViewModel));
myAutoScrollToSourceHandler = new MyAutoScrollToSourceHandler();
myAutoScrollFromSourceHandler = new MyAutoScrollFromSourceHandler(myProject, this);
@@ -1012,21 +995,13 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
if (event.getClickCount() != 1 || event.getID() != MouseEvent.MOUSE_PRESSED) return;
Optional.ofNullable(getClosestPathForLocation(event.getX(), event.getY()))
.map(path -> getPathBounds(path))
.filter(bounds -> event.getX() >= bounds.x)
.filter(bounds -> event.getX() >= bounds.x && event.getY() < bounds.y + bounds.height)
.ifPresent(pathBounds -> {
int scrollDy = 0;
if (getParent().getParent() instanceof JBScrollPane scrollParent) {
scrollDy = scrollParent.getVerticalScrollBar().getValue();
}
floatingToolbar.hideImmediately();
floatingToolbar.setBoundsWithScrollingDy(
getParent().getBounds().width - 50,
pathBounds.y - 5,
40,
pathBounds.height + 5,
scrollDy
);
floatingToolbar.scheduleShow();
floatingToolbar.repaintOnYWithDy(pathBounds.y, scrollDy);
});
}
@@ -1218,12 +1193,19 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
}
}
private static class MyLayeredPane extends JBLayeredPane {
private class MyLayeredPane extends JBLayeredPane {
private final JComponent mainComponent;
private final boolean isLogical;
MyLayeredPane(JComponent mainComponent) {
MyLayeredPane(JScrollPane mainComponent, boolean isLogical) {
this.mainComponent = mainComponent;
this.isLogical = isLogical;
floatingToolbar = new StructureViewFloatingToolbar(this, StructureViewComponent.this);
add(mainComponent, DEFAULT_LAYER);
add(floatingToolbar, POPUP_LAYER);
mainComponent.getVerticalScrollBar().addAdjustmentListener(event -> floatingToolbar.setScrollingDy(event.getValue()));
}
@Override
@@ -1231,7 +1213,7 @@ public class StructureViewComponent extends SimpleToolWindowPanel implements Tre
Rectangle bounds = getBounds();
for (Component component : getComponents()) {
if (component == mainComponent) {
component.setBounds(0, 0, bounds.width, bounds.height);
component.setBounds(isLogical ? 12 : 0, 0, bounds.width, bounds.height);
}
}
}

View File

@@ -0,0 +1,58 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.ide.structureView.newStructureView
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.*
import com.intellij.openapi.editor.toolbar.floating.AbstractFloatingToolbarComponent
import com.intellij.openapi.observable.util.whenMouseMoved
import com.intellij.openapi.ui.isComponentUnderMouse
import com.intellij.openapi.ui.isFocusAncestor
import java.awt.Rectangle
import javax.swing.BorderFactory
import javax.swing.JComponent
internal class StructureViewFloatingToolbar(
private val ownerComponent: JComponent,
private val parentDisposable: Disposable,
) : AbstractFloatingToolbarComponent(
DefaultActionGroup(MergeableActions((ActionManager.getInstance().getAction(ActionPlaces.STRUCTURE_VIEW_FLOATING_TOOLBAR) as ActionGroup))),
parentDisposable
) {
override val autoHideable: Boolean = true
private var boundsWithoutScrolling: Rectangle? = null
override fun isComponentOnHold(): Boolean {
return isComponentUnderMouse() || isFocusAncestor()
}
override fun installMouseMotionWatcher() {
ownerComponent.whenMouseMoved(parentDisposable) {
scheduleShow()
}
}
fun repaintOnYWithDy(y: Int, scrollingDy: Int) {
hideImmediately()
boundsWithoutScrolling = bounds
boundsWithoutScrolling = Rectangle(0, y, minimumButtonSize.width, minimumButtonSize.height)
if (scrollingDy <= 0)
bounds = boundsWithoutScrolling!!
else
setBounds(0, y - scrollingDy, minimumButtonSize.width, minimumButtonSize.height)
scheduleShow()
}
fun setScrollingDy(scrollingDy: Int) {
val bounds = boundsWithoutScrolling ?: bounds
setBounds(bounds.x, bounds.y - scrollingDy, bounds.width, bounds.height)
}
init {
init(ownerComponent)
showingTime = 150
hidingTime = 50
border = BorderFactory.createEmptyBorder()
}
}

View File

@@ -6,20 +6,16 @@ import com.intellij.openapi.actionSystem.ActionGroup
import com.intellij.openapi.observable.util.whenMouseMoved
import com.intellij.openapi.ui.isComponentUnderMouse
import com.intellij.openapi.ui.isFocusAncestor
import org.jetbrains.annotations.ApiStatus
import java.awt.Rectangle
import javax.swing.JComponent
class FloatingToolbar(
private val ownerComponent: JComponent,
actionGroup: ActionGroup,
private val parentDisposable: Disposable,
private val parentDisposable: Disposable
) : AbstractFloatingToolbarComponent(actionGroup, parentDisposable) {
override val autoHideable: Boolean = true
private var boundsWithoutScrolling: Rectangle? = null
override fun isComponentOnHold(): Boolean {
return isComponentUnderMouse() || isFocusAncestor()
}
@@ -30,38 +26,7 @@ class FloatingToolbar(
}
}
@ApiStatus.Internal
fun setBoundsWithScrollingDy(x: Int, y: Int, width: Int, height: Int, scrollingDy: Int) {
boundsWithoutScrolling = bounds
boundsWithoutScrolling = Rectangle(x, y, width, height)
if (scrollingDy <= 0)
bounds = boundsWithoutScrolling!!
else
setBounds(x, y - scrollingDy, width, height)
}
@ApiStatus.Internal
fun setScrollingDy(scrollingDy: Int) {
val bounds = boundsWithoutScrolling ?: bounds
setBounds(bounds.x, bounds.y - scrollingDy, bounds.width, bounds.height)
}
@ApiStatus.Internal
fun setX(x: Int) {
val oldBounds = boundsWithoutScrolling ?: bounds
setBoundsWithScrollingDy(
x,
oldBounds.getY().toInt(),
oldBounds.getWidth().toInt(),
oldBounds.getHeight().toInt(),
oldBounds.getY().toInt() - bounds.getY().toInt()
)
}
init {
init(ownerComponent)
showingTime = 150
hidingTime = 50
//backgroundAlpha = 1F
}
}

View File

@@ -968,7 +968,7 @@
</group>
<group id="StructureViewToolbar"/>
<group id="StructureViewFloatingToolbar"/>
<group id="StructureViewFloatingToolbar" popup="true"/>
<group id="StructureViewPopupMenu">
<reference ref="EditSource"/>

View File

@@ -22,10 +22,12 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiTarget
import com.intellij.ui.SimpleTextAttributes
import org.jetbrains.annotations.ApiStatus
import java.util.concurrent.ConcurrentHashMap
import javax.swing.Icon
internal class LogicalStructureViewModel private constructor(psiFile: PsiFile, editor: Editor?, assembledModel: LogicalStructureAssembledModel<*>, elementBuilder: ElementsBuilder)
@ApiStatus.Internal
class LogicalStructureViewModel private constructor(psiFile: PsiFile, editor: Editor?, assembledModel: LogicalStructureAssembledModel<*>, elementBuilder: ElementsBuilder)
: StructureViewModelBase(psiFile, editor, elementBuilder.createViewTreeElement(assembledModel)),
StructureViewModel.ElementInfoProvider, StructureViewModel.ExpandInfoProvider, StructureViewModel.ActionHandler {
@@ -67,6 +69,12 @@ interface LogicalStructureViewTreeElement<T> : StructureViewTreeElement {
fun getLogicalAssembledModel(): LogicalStructureAssembledModel<T>
/**
* Means that the element is not the node for logical object itself but it's a child which has no own logical object
*/
@ApiStatus.Internal
fun isHasNoOwnLogicalModel(): Boolean = false
}
private class ElementsBuilder {
@@ -275,6 +283,8 @@ private class ElementsBuilder {
override fun getLogicalAssembledModel(): LogicalStructureAssembledModel<T> = parentAssembledModel
override fun isHasNoOwnLogicalModel(): Boolean = true
override fun equals(other: Any?): Boolean {
if (other !is LogicalGroupStructureElement<*>) return false
return parentAssembledModel == other.parentAssembledModel && grouper == other.grouper