With split panes, toolbar action apply to the selected / active editor

Answered

Suppose two diagrams are displayed side-to-side. Eg

Here, the right editor is selected (notice the blue line on the editor tab).

When toolbar icon is interacted on the right editor, the action will apply on the left editor which is the selected editor.

My code to find the editor is quite simple

fun AnActionEvent.findEditor(): ExcalidrawEditor? {
val project = this.project ?: return null
return FileEditorManager.getInstance(project).selectedEditor as? ExcalidrawEditor ?: return null
}

I supposed that getSelectedEditor would return the selected editor.

If someone can advise on what I can do to improve the situation.

8 comments
Comment actions Permalink

Please specify your IDE version. Do you set com.intellij.openapi.actionSystem.ActionToolbar#setTargetComponent in your action toolbar registration?

 

0
Comment actions Permalink

Yann Cebron Hi Yes sorry for that

I'm developing the plugin against version 2020.3.

Do you set com.intellij.openapi.actionSystem.ActionToolbar#setTargetComponent in your action toolbar registration?

Yes, here's the relevant (simplified) code.

class ExcalidrawEditor(
private val project: Project,
private val file: VirtualFile
) : FileEditor,
EditorColorsListener,
DumbAware {

...
val toolbarAndWebView = object : JPanel(BorderLayout()) {
init {
actionPanel.setTargetComponent(viewController.component) // this delegates to the ActionToolbar
add(actionPanel, BorderLayout.NORTH)
add(viewController.component, BorderLayout.CENTER)
}
}
...
}

 

`viewController.component` is a `MultiPanel` that has a `JCEFHtmlPanel`.

 

0
Comment actions Permalink

This might be caused by JCEF/Focus issues. Could you please debug around com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl#getActiveSplittersSync ?

0
Comment actions Permalink

Hi Yann Cebron

Sorry for the late reply, I took some time away from the keyboard, then just started another job, and postponed on this issue.

So what debug showed me at this point in FileEditorManagerImpl#getActiveSplittersSync is that these lines seems to bring the JCEF component of the right pane instead of the left pane.

IdeFocusManager fm = IdeFocusManager.getInstance(myProject);
Component focusOwner = fm.getFocusOwner();

When following the getFocusOwner method up to com.intellij.openapi.wm.impl.FocusManagerImpl#getFocusOwner, I noticed that this code brings the JCEF component.

if (!ApplicationManager.getApplication().isActive()) {
IdeFrame frame = getLastFocusedFrame();
if (frame != null) {
LOG.assertTrue(frame instanceof Window);
}
result = myLastFocusedAtDeactivation.get(frame);
}
else if (myRunContext != null) {
result = (Component)myRunContext.getData(PlatformDataKeys.CONTEXT_COMPONENT.getName());
}
if (result == null) {
result = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); <---------- returns JCEF component
}
if (result == null) {
final Component permOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getPermanentFocusOwner();
if (permOwner != null) {
result = permOwner;
}
if (UIUtil.isMeaninglessFocusOwner(result)) {
result = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
}
}

 

Technically this is not erroneous as the right pane is indeed the one that is selected and active, when the toolbar action icon is clicked on the right editor.

Maybe the current code may miss another piece to be able to find the relevant editor or, I need to use a different way to get the relevant editor than getSelectedEditor

FileEditorManager.getInstance(project).selectedEditor

 

 

 

 

 

 

 

0
Comment actions Permalink

After some debugging, I noticed there's some interesting contextual data in the event, in particular psi.File. Using this information it is possible to find the correct editor from selectedEditors (Notice the s). So I replaced this code

fun AnActionEvent.findEditor(): ExcalidrawEditor? {
val project = this.project ?: return null
return FileEditorManager.getInstance(project).selectedEditor as? ExcalidrawEditor ?: return null
}

By

fun AnActionEvent.findEditor(): ExcalidrawEditor? {
val project = this.project ?: return null
val psiFile = (this.dataContext.getData("psi.File") as PsiFile)
val editor = FileEditorManager.getInstance(project).selectedEditors.find {
psiFile.virtualFile.equals(it.file)
}

return editor as? ExcalidrawEditor ?: return null;
}

This code works well however I wonder if the psi.File (or CommonDataKeys.PSI_FILE) as contextual data of such event will last.

 

Anyway, thank you for looking into this problem.

 

 

 

 

0
Comment actions Permalink

Hi, Brice, have you tried

override fun actionPerformed(e: AnActionEvent) {
val editor = CommonDataKeys.EDITOR.getData(e.dataContext)
...
}

in your toolbar actions?

0
Comment actions Permalink

Hi Vasily

This returns `null`.

The only values in `myDataContext.myCachedData` are :

editor -> {DataManagerImpl$NullResult@34606} 
psi.File -> {JsonFileImpl@30596} "JsonFile: random.excalidraw"
project -> {ProjectExImpl@30586} Project(name=project-fixture, containerState=COMPONENT_CREATED, componentStore=/Users/brice.dutheil/opensource/excalidraw-jetbrains-plugin/project-fixture)
0

Please sign in to leave a comment.