mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
PY-73608 Jupyter(feat): Cell Manager: Rewrite event system v2
GitOrigin-RevId: 14ca6dab713368fa0dea26bddbf31d95a561464c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
979c853600
commit
abc0f80620
@@ -86,10 +86,6 @@ interface NotebookCellInlayController {
|
||||
|
||||
fun createGutterRendererLineMarker(editor: EditorEx, interval: NotebookCellLines.Interval, cellView: EditorCellView) {}
|
||||
|
||||
/**
|
||||
* This method will be called in folding batch operation
|
||||
*/
|
||||
fun updateCellFolding() = Unit
|
||||
|
||||
val shouldUpdateInlay: Boolean
|
||||
get() = false
|
||||
|
||||
@@ -20,11 +20,11 @@ import com.intellij.util.EventDispatcher
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.concurrency.ThreadingAssertions
|
||||
import org.jetbrains.plugins.notebooks.ui.isFoldingEnabledKey
|
||||
import org.jetbrains.plugins.notebooks.visualization.inlay.JupyterBoundsChangeHandler
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.*
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.EditorCellEventListener.*
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.EditorCellViewEventListener.CellViewCreated
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.EditorCellViewEventListener.CellViewRemoved
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.keepScrollingPositionWhile
|
||||
import java.util.*
|
||||
|
||||
class NotebookCellInlayManager private constructor(
|
||||
@@ -232,6 +232,7 @@ class NotebookCellInlayManager private constructor(
|
||||
}.toMutableList()
|
||||
}
|
||||
|
||||
JupyterBoundsChangeHandler.get(editor)?.postponeUpdates()
|
||||
_cells.forEach {
|
||||
it.view?.postInitInlays()
|
||||
}
|
||||
@@ -240,6 +241,7 @@ class NotebookCellInlayManager private constructor(
|
||||
|
||||
cellEventListeners.multicaster.onEditorCellEvents(_cells.map { CellCreated(it) })
|
||||
inlaysChanged()
|
||||
JupyterBoundsChangeHandler.get(editor)?.performPostponed()
|
||||
}
|
||||
|
||||
private fun createCell(interval: NotebookIntervalPointer) = EditorCell(editor, this, interval) { cell ->
|
||||
@@ -250,10 +252,10 @@ class NotebookCellInlayManager private constructor(
|
||||
changedListener?.inlaysChanged()
|
||||
}
|
||||
|
||||
private fun updateCellsFolding(editorCells: List<EditorCell>) = update { ctx ->
|
||||
private fun updateCellsFolding(editorCells: List<EditorCell>) = update { updateContext ->
|
||||
val cellsForFoldingUpdate = editorCells.filter { it.view?.shouldUpdateFolding == true }
|
||||
cellsForFoldingUpdate.forEach { cell ->
|
||||
cell.view?.updateCellFolding(ctx)
|
||||
cell.view?.updateCellFolding(updateContext)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,22 +381,6 @@ class NotebookCellInlayManager private constructor(
|
||||
invalidationListeners.forEach { it.run() }
|
||||
}
|
||||
}
|
||||
|
||||
fun validateCells() {
|
||||
if (!valid) {
|
||||
_cells.forEach {
|
||||
it.view?.also { view ->
|
||||
view.bounds = view.calculateBounds()
|
||||
view.validate()
|
||||
}
|
||||
}
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
fun onInvalidate(function: () -> Unit) {
|
||||
invalidationListeners.add(function)
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateContext(val force: Boolean = false) {
|
||||
@@ -410,5 +396,4 @@ class UpdateContext(val force: Boolean = false) {
|
||||
foldingOperations.forEach { it() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package org.jetbrains.plugins.notebooks.visualization.inlay
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.Inlay
|
||||
import com.intellij.openapi.editor.InlayModel
|
||||
import com.intellij.openapi.editor.ex.FoldingListener
|
||||
import com.intellij.openapi.editor.ex.SoftWrapChangeListener
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.util.EventDispatcher
|
||||
import org.jetbrains.plugins.notebooks.visualization.NotebookCellLines
|
||||
import org.jetbrains.plugins.notebooks.visualization.NotebookCellLinesEvent
|
||||
import java.beans.PropertyChangeListener
|
||||
|
||||
class JupyterBoundsChangeHandler(val editor: EditorImpl) : Disposable {
|
||||
private var isDelayed = false
|
||||
private var isShouldBeRecalculated = false
|
||||
|
||||
private val dispatcher = EventDispatcher.create(JupyterBoundsChangeListener::class.java)
|
||||
|
||||
|
||||
init {
|
||||
editor.addPropertyChangeListener(PropertyChangeListener { _ ->
|
||||
boundsChanged()
|
||||
}, this)
|
||||
|
||||
|
||||
editor.softWrapModel.addSoftWrapChangeListener(object : SoftWrapChangeListener {
|
||||
override fun softWrapsChanged() {
|
||||
}
|
||||
|
||||
override fun recalculationEnds() {
|
||||
if (editor.document.isInEventsHandling)
|
||||
return
|
||||
boundsChanged()
|
||||
}
|
||||
})
|
||||
|
||||
editor.foldingModel.addListener(object : FoldingListener {
|
||||
override fun onFoldProcessingEnd() {
|
||||
boundsChanged()
|
||||
}
|
||||
}, this)
|
||||
|
||||
NotebookCellLines.get(editor).intervalListeners.addListener(object : NotebookCellLines.IntervalListener {
|
||||
override fun documentChanged(event: NotebookCellLinesEvent) {
|
||||
if (event.isIntervalsChanged())
|
||||
boundsChanged()
|
||||
}
|
||||
})
|
||||
|
||||
editor.inlayModel.addListener(object : InlayModel.Listener {
|
||||
override fun onBatchModeFinish(editor: Editor) = boundsChanged()
|
||||
override fun onAdded(inlay: Inlay<*>) {
|
||||
if (inlay.heightInPixels == 0)
|
||||
return
|
||||
|
||||
recalculateIfNotBatch()
|
||||
}
|
||||
|
||||
override fun onUpdated(inlay: Inlay<*>, changeFlags: Int) {
|
||||
if ((changeFlags and InlayModel.ChangeFlags.HEIGHT_CHANGED) != 0)
|
||||
recalculateIfNotBatch()
|
||||
}
|
||||
|
||||
override fun onRemoved(inlay: Inlay<*>) = recalculateIfNotBatch()
|
||||
|
||||
private fun recalculateIfNotBatch() {
|
||||
if (editor.inlayModel.isInBatchMode)
|
||||
return
|
||||
if (editor.document.isInEventsHandling)
|
||||
return
|
||||
boundsChanged()
|
||||
}
|
||||
}, this)
|
||||
}
|
||||
|
||||
override fun dispose() {}
|
||||
|
||||
fun subscribe(listener: JupyterBoundsChangeListener) {
|
||||
dispatcher.addListener(listener)
|
||||
}
|
||||
|
||||
fun unsubscribe(listener: JupyterBoundsChangeListener) {
|
||||
dispatcher.removeListener(listener)
|
||||
}
|
||||
|
||||
|
||||
fun boundsChanged() {
|
||||
if (isDelayed) {
|
||||
isShouldBeRecalculated = true
|
||||
return
|
||||
}
|
||||
dispatcher.multicaster.boundsChanged()
|
||||
}
|
||||
|
||||
fun postponeUpdates() {
|
||||
isDelayed = true
|
||||
isShouldBeRecalculated = false
|
||||
}
|
||||
|
||||
fun performPostponed() {
|
||||
isDelayed = false
|
||||
if (isShouldBeRecalculated) {
|
||||
boundsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val INSTANCE_KEY = Key<JupyterBoundsChangeHandler>("INLAYS_CHANGE_HANDLER")
|
||||
|
||||
fun install(editor: EditorImpl) {
|
||||
val updater = JupyterBoundsChangeHandler(editor).also { Disposer.register(editor.disposable, it) }
|
||||
editor.putUserData(INSTANCE_KEY, updater)
|
||||
}
|
||||
|
||||
fun get(editor: Editor): JupyterBoundsChangeHandler? = INSTANCE_KEY.get(editor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.jetbrains.plugins.notebooks.visualization.inlay
|
||||
|
||||
import java.util.*
|
||||
|
||||
interface JupyterBoundsChangeListener: EventListener {
|
||||
fun boundsChanged() {}
|
||||
}
|
||||
@@ -5,33 +5,19 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.editor.FoldRegion
|
||||
import com.intellij.openapi.editor.ex.FoldingModelEx
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.util.asSafely
|
||||
import org.jetbrains.plugins.notebooks.visualization.NotebookCellInlayController
|
||||
import org.jetbrains.plugins.notebooks.visualization.UpdateContext
|
||||
import java.awt.Rectangle
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import javax.swing.JComponent
|
||||
|
||||
class ControllerEditorCellViewComponent(
|
||||
internal val controller: NotebookCellInlayController,
|
||||
private val parent: EditorCellInput,
|
||||
private val editor: Editor,
|
||||
private val cell: EditorCell,
|
||||
) : EditorCellViewComponent(), HasGutterIcon {
|
||||
|
||||
private val listener = object : ComponentAdapter() {
|
||||
override fun componentResized(e: ComponentEvent) {
|
||||
parent.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
private var foldedRegion: FoldRegion? = null
|
||||
|
||||
init {
|
||||
controller.inlay.renderer.asSafely<JComponent>()?.addComponentListener(listener)
|
||||
}
|
||||
|
||||
override fun updateGutterIcons(gutterAction: AnAction?) {
|
||||
val inlay = controller.inlay
|
||||
inlay.putUserData(NotebookCellInlayController.GUTTER_ACTION_KEY, gutterAction)
|
||||
@@ -39,7 +25,6 @@ class ControllerEditorCellViewComponent(
|
||||
}
|
||||
|
||||
override fun doDispose() {
|
||||
controller.inlay.renderer.asSafely<JComponent>()?.removeComponentListener(listener)
|
||||
controller.let { controller -> Disposer.dispose(controller.inlay) }
|
||||
disposeFolding()
|
||||
}
|
||||
@@ -57,8 +42,7 @@ class ControllerEditorCellViewComponent(
|
||||
}
|
||||
|
||||
override fun calculateBounds(): Rectangle {
|
||||
val component = controller.inlay.renderer as JComponent
|
||||
return component.bounds
|
||||
return controller.inlay.bounds ?: Rectangle(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
override fun updateCellFolding(updateContext: UpdateContext) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.intellij.openapi.editor.event.*
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.ex.util.EditorScrollingPositionKeeper
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.editor.impl.EditorEmbeddedComponentManager.FullEditorWidthRenderer
|
||||
import com.intellij.openapi.fileEditor.FileEditorState
|
||||
import com.intellij.openapi.fileEditor.FileEditorStateLevel
|
||||
import com.intellij.openapi.fileEditor.TextEditor
|
||||
@@ -19,6 +18,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.plugins.notebooks.ui.editor.actions.command.mode.NotebookEditorMode
|
||||
import org.jetbrains.plugins.notebooks.ui.editor.actions.command.mode.setMode
|
||||
import org.jetbrains.plugins.notebooks.visualization.*
|
||||
import org.jetbrains.plugins.notebooks.visualization.inlay.JupyterBoundsChangeHandler
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.EditorCellViewEventListener.CellViewRemoved
|
||||
import org.jetbrains.plugins.notebooks.visualization.ui.EditorCellViewEventListener.EditorCellViewEvent
|
||||
import java.awt.*
|
||||
@@ -49,9 +49,6 @@ private class DecoratedEditor(private val original: TextEditor, private val mana
|
||||
|
||||
setupEditorComponentWrapper()
|
||||
|
||||
manager.onInvalidate {
|
||||
component.revalidate()
|
||||
}
|
||||
manager.addCellViewEventsListener(object : EditorCellViewEventListener {
|
||||
override fun onEditorCellViewEvents(events: List<EditorCellViewEvent>) {
|
||||
events.asSequence().filterIsInstance<CellViewRemoved>().forEach {
|
||||
@@ -146,7 +143,7 @@ private class DecoratedEditor(private val original: TextEditor, private val mana
|
||||
val scrollPane = editorEx.scrollPane
|
||||
val view = scrollPane.viewport.view
|
||||
scrollPane.viewport.isOpaque = false
|
||||
scrollPane.viewport.view = EditorComponentWrapper(editorEx, view as EditorComponentImpl, manager)
|
||||
scrollPane.viewport.view = EditorComponentWrapper(editorEx, view as EditorComponentImpl)
|
||||
}
|
||||
|
||||
override fun getComponent(): JComponent = component
|
||||
@@ -214,13 +211,9 @@ private class DecoratedEditor(private val original: TextEditor, private val mana
|
||||
}
|
||||
|
||||
private fun getCellViewByPoint(point: Point): EditorCellView? {
|
||||
val cells = manager.cells
|
||||
val currentOverCell = cells.filter { it.visible }.mapNotNull { it.view }.firstOrNull {
|
||||
val viewTop = it.bounds.y
|
||||
val viewBottom = viewTop + it.bounds.height
|
||||
point.y in viewTop..<viewBottom
|
||||
}
|
||||
return currentOverCell
|
||||
val visualLine = editor.xyToLogicalPosition(point)
|
||||
val cur = manager.cells.firstOrNull { it.interval.lines.contains(visualLine.line) }
|
||||
return cur?.view
|
||||
}
|
||||
|
||||
override fun inlayClicked(clickedCell: NotebookCellLines.Interval, ctrlPressed: Boolean, shiftPressed: Boolean) {
|
||||
@@ -228,6 +221,7 @@ private class DecoratedEditor(private val original: TextEditor, private val mana
|
||||
mousePressed(clickedCell, ctrlPressed, shiftPressed)
|
||||
}
|
||||
|
||||
@Suppress("ConvertArgumentToSet")
|
||||
private fun mousePressed(clickedCell: NotebookCellLines.Interval, ctrlPressed: Boolean, shiftPressed: Boolean) {
|
||||
val model = editor.cellSelectionModel!!
|
||||
when {
|
||||
@@ -287,8 +281,7 @@ internal fun keepScrollingPositionWhile(editor: Editor, task: Runnable) {
|
||||
|
||||
class EditorComponentWrapper(
|
||||
private val editor: Editor,
|
||||
private val editorComponent: EditorComponentImpl,
|
||||
private val manager: NotebookCellInlayManager,
|
||||
editorComponent: EditorComponentImpl,
|
||||
) : JComponent() {
|
||||
init {
|
||||
isOpaque = false
|
||||
@@ -296,21 +289,11 @@ class EditorComponentWrapper(
|
||||
add(editorComponent, BorderLayout.CENTER)
|
||||
}
|
||||
|
||||
override fun doLayout() {
|
||||
super.doLayout()
|
||||
// EditorEmbeddedComponentManager breaks the Swing layout model as it expect that subcomponents will define their own bounds themselves.
|
||||
// Here we invoke FullEditorWidthRenderer#validate to place inlay components correctly after doLayout.
|
||||
editorComponent.components.asSequence()
|
||||
.filterIsInstance<FullEditorWidthRenderer>()
|
||||
.forEach {
|
||||
it.validate()
|
||||
}
|
||||
manager.validateCells()
|
||||
}
|
||||
|
||||
override fun validateTree() {
|
||||
keepScrollingPositionWhile(editor) {
|
||||
JupyterBoundsChangeHandler.get(editor)?.postponeUpdates()
|
||||
super.validateTree()
|
||||
JupyterBoundsChangeHandler.get(editor)?.performPostponed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.jetbrains.plugins.notebooks.visualization.ui
|
||||
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.ui.ExperimentalUI
|
||||
import com.intellij.ui.paint.LinePainter2D
|
||||
import com.intellij.ui.paint.RectanglePainter2D
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.plugins.notebooks.ui.visualization.notebookAppearance
|
||||
import org.jetbrains.plugins.notebooks.visualization.inlay.JupyterBoundsChangeHandler
|
||||
import org.jetbrains.plugins.notebooks.visualization.inlay.JupyterBoundsChangeListener
|
||||
import org.jetbrains.plugins.notebooks.visualization.use
|
||||
import java.awt.*
|
||||
import java.awt.event.MouseAdapter
|
||||
@@ -14,7 +16,7 @@ import javax.swing.JComponent
|
||||
|
||||
@ApiStatus.Internal
|
||||
class EditorCellFoldingBar(
|
||||
private val editor: EditorEx,
|
||||
private val editor: EditorImpl,
|
||||
private val yAndHeightSupplier: () -> Pair<Int, Int>,
|
||||
private val toggleListener: () -> Unit,
|
||||
) {
|
||||
@@ -22,6 +24,12 @@ class EditorCellFoldingBar(
|
||||
// Because it is not possible to create RangeHighlighter for every single inlay, only on a chain of consequential inlays.
|
||||
private var panel: JComponent? = null
|
||||
|
||||
private val boundsChangeListener = object : JupyterBoundsChangeListener {
|
||||
override fun boundsChanged() {
|
||||
updateBounds()
|
||||
}
|
||||
}
|
||||
|
||||
var visible: Boolean
|
||||
get() = panel?.isVisible == true
|
||||
set(value) {
|
||||
@@ -31,6 +39,7 @@ class EditorCellFoldingBar(
|
||||
val panel = createFoldingBar()
|
||||
editor.gutterComponentEx.add(panel)
|
||||
this.panel = panel
|
||||
JupyterBoundsChangeHandler.get(editor)?.subscribe(boundsChangeListener)
|
||||
updateBounds()
|
||||
}
|
||||
else {
|
||||
@@ -52,6 +61,7 @@ class EditorCellFoldingBar(
|
||||
remove(it)
|
||||
repaint()
|
||||
}
|
||||
JupyterBoundsChangeHandler.get(editor)?.unsubscribe(boundsChangeListener)
|
||||
panel = null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.jetbrains.plugins.notebooks.visualization.ui
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.editor.EditorKind
|
||||
import com.intellij.openapi.editor.FoldRegion
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.plugins.notebooks.ui.visualization.notebookAppearance
|
||||
@@ -11,7 +11,7 @@ import org.jetbrains.plugins.notebooks.visualization.NotebookCellLines
|
||||
import java.awt.Rectangle
|
||||
|
||||
class EditorCellInput(
|
||||
private val editor: EditorEx,
|
||||
private val editor: EditorImpl,
|
||||
private val componentFactory: (EditorCellInput, EditorCellViewComponent?) -> EditorCellViewComponent,
|
||||
private val cell: EditorCell,
|
||||
) : EditorCellViewComponent() {
|
||||
@@ -44,13 +44,21 @@ class EditorCellInput(
|
||||
|
||||
private var gutterAction: AnAction? = null
|
||||
|
||||
private fun getFoldingBounds() : Pair<Int, Int> {
|
||||
private fun getFoldingBounds(): Pair<Int, Int> {
|
||||
//For disposed
|
||||
cell
|
||||
if (cell.intervalPointer.get()==null) {
|
||||
return Pair(0,0)
|
||||
}
|
||||
|
||||
val delimiterPanelSize = if (interval.ordinal == 0) {
|
||||
editor.notebookAppearance.aboveFirstCellDelimiterHeight
|
||||
}
|
||||
else {
|
||||
editor.notebookAppearance.cellBorderHeight / 2
|
||||
}
|
||||
|
||||
val bounds = calculateBounds()
|
||||
return bounds.y + delimiterPanelSize to bounds.height - delimiterPanelSize
|
||||
}
|
||||
|
||||
@@ -63,7 +71,6 @@ class EditorCellInput(
|
||||
toggleTextFolding()
|
||||
inputComponentFactory(this, component)
|
||||
}
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun toggleTextFolding() {
|
||||
@@ -127,17 +134,9 @@ class EditorCellInput(
|
||||
return bounds
|
||||
}
|
||||
|
||||
override fun doLayout() {
|
||||
super.doLayout()
|
||||
folding.updateBounds()
|
||||
}
|
||||
|
||||
fun updateInput(force: Boolean = false) {
|
||||
val oldComponent = if (force) null else component
|
||||
component = componentFactory(this, oldComponent)
|
||||
|
||||
if (bounds != calculateBounds()) {
|
||||
invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,30 +3,22 @@ package org.jetbrains.plugins.notebooks.visualization.ui
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.DataKey
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.impl.EditorImpl
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.ui.AncestorListenerAdapter
|
||||
import com.intellij.ui.ComponentUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.plugins.notebooks.ui.isFoldingEnabledKey
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.NotebookOutputComponentFactory.Companion.gutterPainter
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.NotebookOutputInlayShowable
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.impl.CollapsingComponent
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.impl.SurroundingComponent
|
||||
import java.awt.Graphics
|
||||
import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.SwingUtilities
|
||||
import javax.swing.event.AncestorEvent
|
||||
|
||||
val NOTEBOOK_CELL_OUTPUT_DATA_KEY = DataKey.create<EditorCellOutput>("NOTEBOOK_CELL_OUTPUT")
|
||||
|
||||
class EditorCellOutput internal constructor(
|
||||
private val editor: EditorEx,
|
||||
private val editor: EditorImpl,
|
||||
private val component: CollapsingComponent,
|
||||
private val toDispose: Disposable?,
|
||||
) : EditorCellViewComponent() {
|
||||
@@ -55,8 +47,8 @@ class EditorCellOutput internal constructor(
|
||||
fun getOutputComponent(): JComponent = component.mainComponent
|
||||
|
||||
private fun getFoldingBounds(): Pair<Int, Int> {
|
||||
val inlayComponentLocation = SwingUtilities.convertPoint(component, Point(0, 0), editor.gutterComponentEx)
|
||||
return inlayComponentLocation.y to component.height
|
||||
val bounds = calculateBounds()
|
||||
return bounds.y to bounds.height
|
||||
}
|
||||
|
||||
override fun doDispose() {
|
||||
@@ -67,7 +59,6 @@ class EditorCellOutput internal constructor(
|
||||
override fun doViewportChange() {
|
||||
val component = component.mainComponent as? NotebookOutputInlayShowable ?: return
|
||||
if (component !is JComponent) return
|
||||
validateComponent(component)
|
||||
val componentRect = SwingUtilities.convertRectangle(component, component.bounds, editor.scrollPane.viewport.view)
|
||||
component.shown = editor.scrollPane.viewport.viewRect.intersects(componentRect)
|
||||
}
|
||||
@@ -87,7 +78,16 @@ class EditorCellOutput internal constructor(
|
||||
}
|
||||
|
||||
override fun calculateBounds(): Rectangle {
|
||||
val location = SwingUtilities.convertPoint(component.parent, component.location, editor.contentComponent)
|
||||
val allCellOutputs = parent as EditorCellOutputs
|
||||
|
||||
//Need validate because swing component can be invalid on update
|
||||
allCellOutputs.innerComponent.validate()
|
||||
|
||||
val inlayBounds = allCellOutputs.inlay?.bounds ?: Rectangle(0, 0, 0, 0)
|
||||
val diffBetweenInternalAndExternal = allCellOutputs.innerComponent.location
|
||||
|
||||
val location = component.location
|
||||
location.translate(inlayBounds.x + diffBetweenInternalAndExternal.x, inlayBounds.y + diffBetweenInternalAndExternal.y)
|
||||
return Rectangle(location, component.size)
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import com.intellij.util.asSafely
|
||||
import org.jetbrains.plugins.notebooks.ui.visualization.notebookAppearance
|
||||
import org.jetbrains.plugins.notebooks.visualization.NotebookCellLines
|
||||
import org.jetbrains.plugins.notebooks.visualization.SwingClientProperty
|
||||
import org.jetbrains.plugins.notebooks.visualization.inlay.JupyterBoundsChangeHandler
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.*
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.NotebookOutputComponentFactory.Companion.gutterPainter
|
||||
import org.jetbrains.plugins.notebooks.visualization.outputs.impl.CollapsingComponent
|
||||
@@ -23,8 +24,6 @@ import java.awt.Graphics
|
||||
import java.awt.Graphics2D
|
||||
import java.awt.Rectangle
|
||||
import java.awt.Toolkit
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import javax.swing.JComponent
|
||||
|
||||
class EditorCellOutputs(
|
||||
@@ -53,7 +52,7 @@ class EditorCellOutputs(
|
||||
val outputs: List<EditorCellOutput>
|
||||
get() = _outputs
|
||||
|
||||
private val innerComponent = InnerComponent().also {
|
||||
internal val innerComponent = InnerComponent().also {
|
||||
it.maxHeight = if (!ApplicationManager.getApplication().isUnitTestMode) {
|
||||
(Toolkit.getDefaultToolkit().screenSize.height * 0.3).toInt()
|
||||
}
|
||||
@@ -65,7 +64,18 @@ class EditorCellOutputs(
|
||||
private val outerComponent = SurroundingComponent.create(editor, innerComponent).also {
|
||||
DataManager.registerDataProvider(it, NotebookCellDataProvider(editor, it, interval))
|
||||
}
|
||||
private var inlay: Inlay<*>? = null
|
||||
|
||||
internal var inlay: Inlay<*>? = null
|
||||
private set(value) {
|
||||
val oldHeight = field?.heightInPixels ?: 0
|
||||
val newHeight = value?.heightInPixels ?: 0
|
||||
|
||||
val shouldUpdate = oldHeight != newHeight
|
||||
field = value
|
||||
|
||||
if (shouldUpdate)
|
||||
JupyterBoundsChangeHandler.get(editor)?.boundsChanged()
|
||||
}
|
||||
|
||||
init {
|
||||
update()
|
||||
@@ -227,15 +237,6 @@ class EditorCellOutputs(
|
||||
priority = editor.notebookAppearance.NOTEBOOK_OUTPUT_INLAY_PRIORITY,
|
||||
offset = computeInlayOffset(editor.document, interval().lines),
|
||||
).also { inlay ->
|
||||
inlay.renderer.asSafely<JComponent>()?.addComponentListener(object : ComponentAdapter() {
|
||||
private fun update() {
|
||||
invalidate()
|
||||
outputs.forEach { it.folding.updateBounds() }
|
||||
}
|
||||
|
||||
override fun componentResized(e: ComponentEvent) = update()
|
||||
override fun componentMoved(e: ComponentEvent?) = update()
|
||||
})
|
||||
Disposer.register(inlay) {
|
||||
onInlayDisposed(this)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.awt.Point
|
||||
import java.awt.Rectangle
|
||||
import java.time.ZonedDateTime
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class EditorCellView(
|
||||
@@ -92,7 +91,7 @@ class EditorCellView(
|
||||
currentComponent
|
||||
}
|
||||
else {
|
||||
ControllerEditorCellViewComponent(controller, parent, editor, cell)
|
||||
ControllerEditorCellViewComponent(controller, editor, cell)
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -109,9 +108,6 @@ class EditorCellView(
|
||||
}
|
||||
|
||||
fun postInitInlays() {
|
||||
_controllers.forEach {
|
||||
(it.inlay.renderer as? JPanel)?.validate()
|
||||
}
|
||||
updateControllers()
|
||||
}
|
||||
|
||||
@@ -152,7 +148,6 @@ class EditorCellView(
|
||||
input.update(force)
|
||||
updateOutputs()
|
||||
updateCellHighlight()
|
||||
invalidate()
|
||||
}
|
||||
|
||||
private fun recreateControllers() {
|
||||
@@ -395,31 +390,20 @@ class EditorCellView(
|
||||
input.runCellButton?.visible = mouseOver || selected
|
||||
}
|
||||
|
||||
override fun doInvalidate() {
|
||||
cellInlayManager.invalidateCells()
|
||||
}
|
||||
|
||||
override fun calculateBounds(): Rectangle {
|
||||
val inputBounds = input.calculateBounds()
|
||||
val currentOutputs = outputs
|
||||
return Rectangle(
|
||||
0,
|
||||
inputBounds.y,
|
||||
editor.contentSize.width,
|
||||
currentOutputs?.calculateBounds()
|
||||
?.takeIf { !it.isEmpty }
|
||||
?.let { it.height + it.y - inputBounds.y }
|
||||
?: inputBounds.height
|
||||
)
|
||||
|
||||
val outputRectangle = currentOutputs?.calculateBounds()?.takeIf { !it.isEmpty }
|
||||
val height = outputRectangle?.let { it.height + it.y - inputBounds.y } ?: inputBounds.height
|
||||
|
||||
return Rectangle(0, inputBounds.y, editor.contentSize.width, height)
|
||||
}
|
||||
|
||||
val shouldUpdateFolding: Boolean
|
||||
get() = controllers.any { it.shouldUpdateInlay }
|
||||
|
||||
override fun updateCellFolding(updateContext: UpdateContext) {
|
||||
super.updateCellFolding(updateContext)
|
||||
controllers.forEach { it.updateCellFolding() }
|
||||
}
|
||||
|
||||
fun updateExecutionStatus(executionCount: Int?, progressStatus: ProgressStatus?, startTime: ZonedDateTime?, endTime: ZonedDateTime?) {
|
||||
_controllers.filterIsInstance<CellExecutionStatusView>().firstOrNull()
|
||||
|
||||
@@ -4,31 +4,18 @@ import org.jetbrains.plugins.notebooks.visualization.UpdateContext
|
||||
import java.awt.Rectangle
|
||||
|
||||
abstract class EditorCellViewComponent {
|
||||
|
||||
var bounds: Rectangle = Rectangle(0, 0, 0, 0)
|
||||
set(value) {
|
||||
if (value != field) {
|
||||
invalidate()
|
||||
}
|
||||
field = value
|
||||
}
|
||||
|
||||
private var parent: EditorCellViewComponent? = null
|
||||
protected var parent: EditorCellViewComponent? = null
|
||||
|
||||
private val children = mutableListOf<EditorCellViewComponent>()
|
||||
|
||||
private var valid = false
|
||||
|
||||
fun add(child: EditorCellViewComponent) {
|
||||
children.add(child)
|
||||
child.parent = this
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun remove(child: EditorCellViewComponent) {
|
||||
children.remove(child)
|
||||
child.parent = null
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
@@ -47,31 +34,6 @@ abstract class EditorCellViewComponent {
|
||||
|
||||
abstract fun calculateBounds(): Rectangle
|
||||
|
||||
fun isValid(): Boolean = valid
|
||||
|
||||
fun validate() {
|
||||
if (!valid) {
|
||||
doLayout()
|
||||
children.forEach { it.validate() }
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
|
||||
open fun doLayout() {
|
||||
children.forEach {
|
||||
it.bounds = it.calculateBounds()
|
||||
}
|
||||
}
|
||||
|
||||
fun invalidate() {
|
||||
if (valid) {
|
||||
doInvalidate()
|
||||
valid = false
|
||||
parent?.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
open fun doInvalidate() = Unit
|
||||
open fun updateCellFolding(updateContext: UpdateContext) {
|
||||
children.forEach {
|
||||
it.updateCellFolding(updateContext)
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.editor.impl.EditorEmbeddedComponentManager
|
||||
import com.intellij.openapi.editor.impl.EditorEmbeddedComponentManager.Properties.RendererFactory
|
||||
import java.awt.Container
|
||||
import java.awt.event.ComponentAdapter
|
||||
import java.awt.event.ComponentEvent
|
||||
import java.awt.event.HierarchyEvent
|
||||
@@ -116,11 +115,3 @@ fun JComponent.yOffsetFromEditor(editor: Editor): Int? =
|
||||
SwingUtilities.convertPoint(this, 0, 0, editor.contentComponent).y
|
||||
.takeIf { it >= 0 }
|
||||
?.let { it + insets.top }
|
||||
|
||||
fun validateComponent(c: Container) {
|
||||
var validationRoot = c
|
||||
while (!validationRoot.isValidateRoot && validationRoot.parent != null) {
|
||||
validationRoot = validationRoot.parent
|
||||
}
|
||||
validationRoot.validate()
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class TextEditorCellViewComponent(
|
||||
|
||||
override fun updateGutterIcons(gutterAction: AnAction?) {
|
||||
disposeExistingHighlighter()
|
||||
if (gutterAction != null && cell.view?.isValid() == true) {
|
||||
if (gutterAction != null) {
|
||||
val markupModel = editor.markupModel
|
||||
val interval = safeInterval ?: return
|
||||
val startOffset = editor.document.getLineStartOffset(interval.lines.first)
|
||||
|
||||
Reference in New Issue
Block a user