mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-317864 Add ScrollableContentBorder implementation for complicated cases
Track scroll panes automatically as they're added and add borders if a scroll pane touching the edge is scrolled. Needed for complicated tool windows like Build, where it's hard to figure out the exact scroll panes to track, because they are added to a splitter, which may or may not have other components and it may change at any moment. So instead track scroll panes and check their location to see if scrolling should add a border. GitOrigin-RevId: 1dae2bf8b5ce43beb54de84a59a58e78721fb3eb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d879451de7
commit
c7e91d822f
@@ -69,7 +69,7 @@ public class SimpleToolWindowPanel extends JBPanelWithEmptyText implements Quick
|
||||
});
|
||||
|
||||
if (ExperimentalUI.isNewUI()) {
|
||||
myScrollPaneTracker = new ScrollPaneTracker(this, this::isInContent, () -> {
|
||||
myScrollPaneTracker = new ScrollPaneTracker(this, this::isInContent, tracker -> {
|
||||
updateScrolledState();
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
@@ -223,7 +223,7 @@ public class SimpleToolWindowPanel extends JBPanelWithEmptyText implements Quick
|
||||
var newState = false;
|
||||
for (ScrollPaneScrolledState scrollPaneState : myScrollPaneTracker.getScrollPaneStates()) {
|
||||
var scrollPane = scrollPaneState.getScrollPane();
|
||||
boolean scrolled = myVertical ? !scrollPaneState.isVerticalAtStart() : !scrollPaneState.isHorizontalAtStart();
|
||||
boolean scrolled = myVertical ? !scrollPaneState.getState().isVerticalAtStart() : !scrollPaneState.getState().isHorizontalAtStart();
|
||||
if (isTouchingToolbar(scrollPane) && scrolled) {
|
||||
newState = true;
|
||||
break;
|
||||
|
||||
@@ -19,6 +19,11 @@ import javax.swing.JScrollPane
|
||||
@Internal
|
||||
data class ScrollPaneScrolledState(
|
||||
val scrollPane: JScrollPane,
|
||||
val state: ScrolledState,
|
||||
)
|
||||
|
||||
@Internal
|
||||
data class ScrolledState(
|
||||
val isHorizontalAtStart: Boolean,
|
||||
val isHorizontalAtEnd: Boolean,
|
||||
val isVerticalAtStart: Boolean,
|
||||
@@ -47,7 +52,7 @@ data class ScrollPaneScrolledState(
|
||||
class ScrollPaneTracker(
|
||||
container: Component,
|
||||
private val filter: (JScrollPane) -> Boolean,
|
||||
private val callback: () -> Unit,
|
||||
private val callback: (ScrollPaneTracker) -> Unit,
|
||||
) {
|
||||
|
||||
val scrollPaneStates: List<ScrollPaneScrolledState>
|
||||
@@ -75,9 +80,9 @@ class ScrollPaneTracker(
|
||||
|
||||
private fun registerScrollPane(scrollPane: JScrollPane) {
|
||||
myTrackers.add(ScrollPaneScrolledStateTracker(scrollPane) {
|
||||
callback()
|
||||
callback(this)
|
||||
})
|
||||
callback()
|
||||
callback(this)
|
||||
}
|
||||
|
||||
private fun unregisterScrollPane(scrollPane: JScrollPane) {
|
||||
@@ -113,10 +118,12 @@ class ScrollPaneScrolledStateTracker(val scrollPane: JScrollPane, private val ca
|
||||
|
||||
var state: ScrollPaneScrolledState = ScrollPaneScrolledState(
|
||||
scrollPane,
|
||||
isHorizontalAtStart = true,
|
||||
isHorizontalAtEnd = true,
|
||||
isVerticalAtStart = true,
|
||||
isVerticalAtEnd = true,
|
||||
ScrolledState(
|
||||
isHorizontalAtStart = true,
|
||||
isHorizontalAtEnd = true,
|
||||
isVerticalAtStart = true,
|
||||
isVerticalAtEnd = true,
|
||||
)
|
||||
); private set
|
||||
|
||||
private val scrollBarListener = PropertyChangeListener { e ->
|
||||
@@ -149,10 +156,12 @@ class ScrollPaneScrolledStateTracker(val scrollPane: JScrollPane, private val ca
|
||||
private fun fireCallback(fireAnyway: Boolean) {
|
||||
val newState = ScrollPaneScrolledState(
|
||||
scrollPane,
|
||||
isHorizontalAtStart = horizontalListener.isAtStart,
|
||||
isHorizontalAtEnd = horizontalListener.isAtEnd,
|
||||
isVerticalAtStart = verticalListener.isAtStart,
|
||||
isVerticalAtEnd = verticalListener.isAtEnd,
|
||||
ScrolledState(
|
||||
isHorizontalAtStart = horizontalListener.isAtStart,
|
||||
isHorizontalAtEnd = horizontalListener.isAtEnd,
|
||||
isVerticalAtStart = verticalListener.isAtStart,
|
||||
isVerticalAtEnd = verticalListener.isAtEnd,
|
||||
)
|
||||
)
|
||||
if (fireAnyway || newState != state) {
|
||||
state = newState
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.beans.PropertyChangeListener
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JScrollPane
|
||||
import javax.swing.SwingUtilities
|
||||
import javax.swing.border.Border
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@@ -78,8 +79,8 @@ class ScrollableContentBorder private constructor(
|
||||
|
||||
val borders = sides.associateWith { side -> ScrollableContentBorder(side.toMask()) }
|
||||
|
||||
val tracker = ScrollPaneScrolledStateTracker(scrollPane) { state ->
|
||||
updateBorderVisibility(targetComponent, borders, state)
|
||||
val tracker = ScrollPaneScrolledStateTracker(scrollPane) { scrollPaneState ->
|
||||
updateBorderVisibility(targetComponent, borders, scrollPaneState.state)
|
||||
}
|
||||
|
||||
targetComponent.border = if (borders.size == 1) borders.values.single() else JBUI.Borders.compound(*borders.values.toTypedArray())
|
||||
@@ -92,6 +93,52 @@ class ScrollableContentBorder private constructor(
|
||||
})
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setup(container: JComponent, side: Side) {
|
||||
setup(container, setOf(side))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setup(container: JComponent, sides: Set<Side>) {
|
||||
val borders = sides.associateWith { side -> ScrollableContentBorder(side.toMask()) }
|
||||
container.border = if (borders.size == 1) borders.values.single() else JBUI.Borders.compound(*borders.values.toTypedArray())
|
||||
ScrollPaneTracker(container, { true }) { tracker ->
|
||||
updateScrollPaneStates(container, borders, tracker)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateScrollPaneStates(
|
||||
container: JComponent,
|
||||
borders: Map<Side, ScrollableContentBorder>,
|
||||
tracker: ScrollPaneTracker,
|
||||
) {
|
||||
for (scrollPaneState in tracker.scrollPaneStates) {
|
||||
ClientProperty.put(scrollPaneState.scrollPane, TARGET_COMPONENT, WeakReference(container))
|
||||
}
|
||||
val state = ScrolledState(
|
||||
isHorizontalAtStart = tracker.noneMatch { atLeft(container, it.scrollPane) && !it.state.isHorizontalAtStart },
|
||||
isHorizontalAtEnd = tracker.noneMatch { atRight(container, it.scrollPane) && !it.state.isHorizontalAtEnd },
|
||||
isVerticalAtStart = tracker.noneMatch { atTop(container, it.scrollPane) && !it.state.isVerticalAtStart },
|
||||
isVerticalAtEnd = tracker.noneMatch { atBottom(container, it.scrollPane) && !it.state.isVerticalAtEnd },
|
||||
)
|
||||
updateBorderVisibility(container, borders, state)
|
||||
}
|
||||
|
||||
private fun ScrollPaneTracker.noneMatch(predicate: (ScrollPaneScrolledState) -> Boolean): Boolean =
|
||||
scrollPaneStates.none { it.scrollPane.isShowing && predicate(it) }
|
||||
|
||||
private fun atLeft(container: JComponent, scrollPane: JScrollPane): Boolean =
|
||||
SwingUtilities.convertPoint(scrollPane.parent, scrollPane.location, container).x == container.insets.left
|
||||
|
||||
private fun atRight(container: JComponent, scrollPane: JScrollPane): Boolean =
|
||||
SwingUtilities.convertRectangle(scrollPane.parent, scrollPane.bounds, container).let { it.x + it.width } == container.width - container.insets.right
|
||||
|
||||
private fun atTop(container: JComponent, scrollPane: JScrollPane): Boolean =
|
||||
SwingUtilities.convertPoint(scrollPane.parent, scrollPane.location, container).y == container.insets.top
|
||||
|
||||
private fun atBottom(container: JComponent, scrollPane: JScrollPane): Boolean =
|
||||
SwingUtilities.convertRectangle(scrollPane.parent, scrollPane.bounds, container).let { it.y + it.height } == container.height - container.insets.bottom
|
||||
|
||||
private fun isOneSideBorder(sideBorder: Border): Boolean {
|
||||
if (sideBorder !is SideBorder) return false
|
||||
|
||||
@@ -108,7 +155,7 @@ class ScrollableContentBorder private constructor(
|
||||
private fun updateBorderVisibility(
|
||||
targetComponent: JComponent,
|
||||
borders: Map<Side, ScrollableContentBorder>,
|
||||
state: ScrollPaneScrolledState,
|
||||
state: ScrolledState,
|
||||
) {
|
||||
var changed = false
|
||||
for ((side, border) in borders) {
|
||||
|
||||
@@ -239,7 +239,7 @@ internal class ToolWindowImpl(val toolWindowManager: ToolWindowManagerImpl,
|
||||
var newState = false
|
||||
for (scrollPaneState in tracker.scrollPaneStates) {
|
||||
val scrollPane = scrollPaneState.scrollPane
|
||||
if (isTouchingHeader(scrollPane) && !scrollPaneState.isVerticalAtStart) {
|
||||
if (isTouchingHeader(scrollPane) && !scrollPaneState.state.isVerticalAtStart) {
|
||||
newState = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user