[PyCharm] Jupyter (fix): Fixed notebook scroll behavior when we have text outputs with scroll. #PY-79747 Fixed

(cherry picked from commit a70b039337f936b0081acbe31e2dbdaaffbe83fa)

GitOrigin-RevId: d7eb140ebaae589b6df47bfe0ab462394a1feda7
This commit is contained in:
Nikita Pavlenko
2025-05-15 11:24:07 +02:00
committed by intellij-monorepo-bot
parent a67a5367b2
commit 4bcc184b26
2 changed files with 27 additions and 24 deletions

View File

@@ -31,7 +31,7 @@ import kotlin.math.min
class DecoratedEditor private constructor(
private val editorImpl: EditorImpl,
private val manager: NotebookCellInlayManager,
) : NotebookEditor, Disposable {
) : NotebookEditor, Disposable.Default {
override val hoveredCell: AtomicProperty<EditorCell?> = AtomicProperty(null)
override val singleFileDiffMode: AtomicProperty<Boolean> = AtomicProperty(false)
@@ -39,21 +39,15 @@ class DecoratedEditor private constructor(
Disposer.register(this, it)
}
init {
wrapEditorComponent(editorImpl)
notebookEditorKey.set(editorImpl, this)
}
override fun dispose() {
}
private fun wrapEditorComponent(editor: EditorImpl) {
val nestedScrollingSupport = NestedScrollingSupportImpl()
NotebookAWTMouseDispatcher(editor.scrollPane).apply {
eventDispatcher.addListener { event ->
if (event is MouseWheelEvent) {
nestedScrollingSupport.processMouseWheelEvent(event)
@@ -81,11 +75,8 @@ class DecoratedEditor private constructor(
if (editorImpl.getMouseEventArea(event) != EditorMouseEventArea.EDITING_AREA) {
editorImpl.setMode(NotebookEditorMode.COMMAND)
}
val shouldConsumeEvent = updateSelectionAfterClick(hoveredCell.interval, event.isCtrlPressed(),
updateSelectionAfterClick(hoveredCell.interval, event.isCtrlPressed(),
event.isShiftPressed(), event.button)
if (shouldConsumeEvent) {
event.consume()
}
}
}
@@ -163,10 +154,7 @@ class DecoratedEditor private constructor(
updateSelectionAfterClick(clickedCell, ctrlPressed, shiftPressed, mouseButton)
}
/**
* Return should event be consumed.
*/
fun updateSelectionAfterClick(clickedCell: NotebookCellLines.Interval, ctrlPressed: Boolean, shiftPressed: Boolean, mouseButton: Int): Boolean {
fun updateSelectionAfterClick(clickedCell: NotebookCellLines.Interval, ctrlPressed: Boolean, shiftPressed: Boolean, mouseButton: Int) {
val model = editorImpl.cellSelectionModel!!
when {
ctrlPressed -> {
@@ -203,10 +191,8 @@ class DecoratedEditor private constructor(
}
mouseButton == MouseEvent.BUTTON1 && !model.isSelectedCell(clickedCell) -> {
model.selectSingleCell(clickedCell)
return false
}
}
return false
}
companion object {
@@ -233,4 +219,4 @@ private fun MouseEvent.isCtrlPressed(): Boolean =
(modifiersEx and if (ClientSystemInfo.isMac()) InputEvent.META_DOWN_MASK else InputEvent.CTRL_DOWN_MASK) != 0
private fun MouseEvent.isShiftPressed(): Boolean =
(modifiersEx and InputEvent.SHIFT_DOWN_MASK) != 0
(modifiersEx and InputEvent.SHIFT_DOWN_MASK) != 0

View File

@@ -48,7 +48,15 @@ class NestedScrollingSupportImpl {
}
}
resetOwnerIfTimeoutExceeded()
val owner = resetOwnerIfEventIsOutside(e)
// We have inlays with error or text outputs. Sometimes they have scroll, sometimes not,
// and in case they have not, or the scroll in the desired direction is impossible, we will scroll the main Editor.
if (owner is JScrollPane && !canScroll(e, owner)) {
resetOwner()
}
if (owner != null) {
if (component != owner) {
redispatchEvent(SwingUtilities.convertMouseEvent(component, e, owner))
@@ -73,17 +81,17 @@ class NestedScrollingSupportImpl {
}
}
private fun dispatchEvent(event: MouseEvent) {
private fun dispatchEvent(event: MouseWheelEvent) {
val owner = event.component
if (isAsync(owner)) return
if (owner is JLayer<*>) {
val aa = owner.parent
if (aa is JLayer<*>) {
dispatchEventSync(event, aa.parent)
val ownerParent = owner.parent
if (ownerParent is JLayer<*>) {
dispatchEventSync(event, ownerParent.parent)
}
else {
dispatchEventSync(event, aa)
dispatchEventSync(event, ownerParent)
}
}
else {
@@ -91,7 +99,16 @@ class NestedScrollingSupportImpl {
}
}
private fun dispatchEventSync(event: MouseEvent, owner: Component) {
private fun canScroll(event: MouseWheelEvent, owner: JScrollPane): Boolean {
return if (event.wheelRotation > 0) { // Down
owner.verticalScrollBar.maximum > owner.verticalScrollBar.value + owner.viewport.height
}
else { // Up
owner.verticalScrollBar.minimum < owner.verticalScrollBar.value
}
}
private fun dispatchEventSync(event: MouseWheelEvent, owner: Component) {
val oldDispatchingEvent = dispatchingEvent
dispatchingEvent = event
try {