Plugin That Create Panel For Specific Editor

I’m trying to write an Atom plugin that add a panel to a text editor. The panel need to she few buttons for compiling the active text editor file and an output text area.
From what I’ve seen so far the only way to add panel in Atom is to the whole workspace which means if the user tab away to another file or close the file the plugin’s panel remain there “detached” from the active editor.

I guess it is possible to simulate the link between the active editor and the panel by listen to workspace events but I think it’s a common requirements.

Any help will be great.

Thank you,
Ido.

The Atom API doesn’t have a way to create a panel that is attached to a specific pane item. Panels are attached to the edges of the pane area. The pane area itself can be subdivided multiple times by splitting panes. If you want to have a panel attached to a specific pane item, then you should probably create a pane item that contains your panel and the text editor as two components. This is the only UI construct that I can think of that wouldn’t have edge cases around split panes and switching tabs.

Thanks. I’ve found minimap plugin which listen to events and add the minimap-element to each editor-element - I’ll try to follow that example.

Late to the party, but I’m trying to achieve the same thing: Create a small panel at the top of an editor. This panel would contain buttons that start processing of the editor contents, and settings (checkbuttons) that modify how that processing is done.

@leedohm, could you expand a bit how I would go about putting my panel and a text editor as two components into a pane item? I haven’t found an Atom container class or similar that would allow that.

Putting the HTML elements together, e.g. a div for my part and an atom-text-editor element for the editor is possible. But that’s not enough to have a working TextEditor, is it?

Moreover, I would like to preserve the editor state, e.g. scroll position, so optimally I would not create a new TextEditor object, but save the existing one, put the container in its place as an item into the pane, and then put the original TextEditor into the container.

@ido-ran, have you figured out how minimap does this?

I tried two different hacky things:

  • Add a p as the first child element of atom-text-editor. The element is visible, but I couldn’t get it to be shown above instead of to the left of gutter & scroll-view.

  • Wrap atom-text-editor inside a div, with a p sibling before:

    editor = atom.workspace.getActiveTextEditor()
    ate = editor.element
    ate.style="height: 90%"
    atep = ate.parentElement
    d = document.createElement('div')
    p = document.createElement('p')
    p.textContent = "hi"
    d.appendChild(p)
    d.appendChild(ate)
    atep.appendChild(d)
    

    That “works” insofar as the editor remains functional, bound to the status bar, asks to save if there are changes. However, without the explicit style the editor element has a height of 0. Moreover, when I switch to another tab, the outer div is assigned display: none (makes sense), but it is not made visible again on switching back. And occasionally my whole hacked structure is replaced by the ordinary editor element.

and back to this one,

What I want is similar to a Block Decoration, but not bound to a specific position in the text, but to the top of the editor element, staying visible when I scroll down.

I tried achieving this by actually putting a block decoration on a marker at [0, 0], and then giving it CSS that fixes it to the top (position: fixed; top: 0). Doesn’t work either. Among other things because elements in the editor apparently dynamically pop into and out of existence.

How about looking at a package like keybinding-resolver.
Notice the use of etch.
https://github.com/atom/keybinding-resolver/blob/master/lib/keybinding-resolver-view.js

Instead of bottom you would be looking for top

Another exemple to work from would be
https://atom.io/packages/find-and-replace

Hi @snoop, thanks for the reply!

Unless I’m missing something, neither Atom’s keybinding resolver nor the find and replace dialog do what I want to do. The idea is to have something that is connected (both functionally & visually) to one specific editor pane. The keybinding resolver is a pane item in a Dock, and the find and replace dialog in a Panel common to all editors.

I will have a closer look at etch. So far it is not clear to me what it provides above and beyond (now-) standard custom HTML elements though. Are Atom’s UI elements, including TextEditor/<atom-text-editor>, typically implemented with etch? The editor doesn’t seem to be a custom HTML element, at least customElements.get('atom-text-editor') returns undefined.

I think I have a snippet of code that does just what you‘re looking for. However, I‘m away for the holidays and can only share it later this week.

1 Like

There isn’t anything built into the Atom API for doing what you want. You would have to build it yourself.

Alright. But what I had hoped for were some pointers how to approach that.

I described several failed attempts above.

I figured it out, posting it here in case anyone else wants to do something like this.
The trick is to insert the element into the editor as its first child, and to use absolute positioning.

JavaScript to create and insert the element:

editorPanel = document.createElement('div')
editorPanel.classList.add('editor-panel')
// add more stuff to the editorPanel
editor.element.prepend(editorPanel)

height = editorPanel.offsetHeight
// make space above editor
editor.element.style.marginTop = height + 'px'    
// move panel into space
editorPanel.style.top = '-' + height + 'px'   

LESS to style the element:

editor-panel {
  position: absolute;
  width: 100%;
  // replace editor style by UI style
  font-family: @font-family;
  font-size: @font-size;
  color: @text-color;
  background-color: @tool-panel-background-color;
}

I used this for a while now, and it seems to be working reliably. Atom doesn’t touch the prepended element or the editor’s top margin, and the element remains in place if the editor pane is moved around, resized, etc. It assumes that the element always retains the same height; if that is not the case, a ResizeObserver can be used to adjust dynamically.

In practice, the JavaScript code should be used in the constructor of a class which implements UI functionality. In that case, it makes sense to have the instance to be accessible from the element, e.g. by

editorPanel.model = this

For an example of this, see my application

@leedohm, maybe you could comment whether this is a reasonable approach, and whether one can expect it to keep working in future Atom versions.

1 Like

The only thing that the Atom team has ever committed to maintaining in future versions is the documented Atom API, and even then, only within reason. So since there isn’t an Atom API method to performing this task, I can’t give any guarantees that this approach will continue working in any future Atom versions.

As far as whether this is reasonable, my personal definition of “reasonable” is largely based on two questions:

  1. Does it work?
  2. Does it break other stuff?

And it sounds like you’ve done your due diligence on both of those counts.

Would I do it this way? Now that I’ve taken the time to take a look at the animation you’ve provided in the package README, I’d probably put the toolbar in the preview pane item rather than in the editor pane item. On the other hand, the reason I would make that choice has to do specifically with the specific use case of creating a preview of the contents of an editor and shouldn’t be considered a commentary on the applicability of the specific technique of how to inject non-editor UI into an editor pane item.

Thanks for the comment, and I understand.

To put the question differently: Are there currently any plans to fundamentally change how atom-text-editor / TextEditor works?

I had an earlier version where I put the UI elements into the PDF viewer, but I didn’t like that because logically this functionality belongs to the editor; it extends it. Pandoc processes the file in the editor, the PDF is just the result. Or, one of the buttons creates a sidecar file with processing options specific to the file in the editor.

The PDF viewer on the other hand is provided by the pdf-view package, but could just as well be provided by another package. Any UI elements there should imho be related to viewing the PDF. (And I’m actually working in parallel on an improved version exposing more of pdf.js’s functionality through a UI in the viewer.)

And to brag a bit (though this is entirely due to Pandoc’s excellence): this is not a preview. The PDF shown is high-quality, ready to be published and printed.

See https://github.com/Aerijo/atom-pdf-view-plus

@Aerijo, yes, I’ve seen that package of yours, but from the description it isn’t clear what the advantage over pdf-view is. What kept me from checking it out is that the installation is complicated by using TypeScript.

Actually, the mentioned earlier version of my package had a PDF viewer built in. It is based on the web viewer of pdf.js (not just the library) and has a proper UI, better than pdf-view. I’m right now in the process of putting it in its own package.

One advantage is it doesn’t have memory leaks, and it uses the pdfjs web viewer like you describe. apm works for install, it’s just the package will be several times larger.

Ah OK. Just installed it, and yes, same approach as the one I had. If I had seen your package earlier, I’d have saved a lot of time!

One thing that is different is that I tried to comply with the request by the pdf.js authors that the viewer shouldn’t be used unmodified, but reskinned: https://mozilla.github.io/pdf.js/getting_started/
I also made it use the current Atom UI theme colors. And I removed the print and bookmark buttons.

Pity for the duplicated effort, but I think I’ll still package my version.