New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Floating windows in TUI and Remote UI #6619

Open
wants to merge 12 commits into
base: master
from

Conversation

@bfredl
Member

bfredl commented Apr 29, 2017

From the drawer of very naive and crazy stuff (one does not simply mess with window positioning and expect stuff to work). Partially based on @dzhou121 external ui stuff (crazy/naiveness mine)

do this in a script:

fnew
call nvim_win_float_set_pos(0,5,10,20,5)
hi Floating guibg=#000044
set winhl=Normal:Floating
set nonumber

@justinmk justinmk added this to the 0.3 milestone Apr 29, 2017

@marvim marvim added the WIP label Apr 29, 2017

Show outdated Hide outdated src/nvim/ex_cmds.lua Outdated

@bfredl bfredl changed the title from [WIP] (very WIP) Floating windows in TUI to [WIP] Floating windows in TUI Apr 30, 2017

@justinmk justinmk referenced this pull request May 2, 2017

Merged

news: version 0.2 #151

@johnzeng

This comment has been minimized.

Show comment
Hide comment
@johnzeng

johnzeng Jun 5, 2017

hi, will this PR be merged? or, still wip?

johnzeng commented Jun 5, 2017

hi, will this PR be merged? or, still wip?

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Jun 5, 2017

Member

Is still WIP, many window commands can easily segfault, and the mouse is broken. Also we need a better redrawing strategy than "redraw the float everytime anything else is redrawn". Probabaly should use some buffering logic from #5686

Member

bfredl commented Jun 5, 2017

Is still WIP, many window commands can easily segfault, and the mouse is broken. Also we need a better redrawing strategy than "redraw the float everytime anything else is redrawn". Probabaly should use some buffering logic from #5686

@johnzeng

This comment has been minimized.

Show comment
Hide comment
@johnzeng

johnzeng Jun 5, 2017

Good to know that. I am waiting for it.

johnzeng commented Jun 5, 2017

Good to know that. I am waiting for it.

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Aug 13, 2017

Member

Update: replaced fnew with api command:

Window nvim_open_floating_window(Buffer buffer,
                                 Integer x, Integer y, Integer w, Integer h,
                                 Integer mode, Boolean enter, Error *err)

mode supports anchor modes (will be relevant for UIs with different grid sizes in floats) as well making the window "unfocusable". It is still possible to make it current with api or number, but the user selecting it "by accident" with mouse or ctrl-Wctrl-W should be avoided.

Also added very basic mouse support.

Member

bfredl commented Aug 13, 2017

Update: replaced fnew with api command:

Window nvim_open_floating_window(Buffer buffer,
                                 Integer x, Integer y, Integer w, Integer h,
                                 Integer mode, Boolean enter, Error *err)

mode supports anchor modes (will be relevant for UIs with different grid sizes in floats) as well making the window "unfocusable". It is still possible to make it current with api or number, but the user selecting it "by accident" with mouse or ctrl-Wctrl-W should be avoided.

Also added very basic mouse support.

@justinmk

This comment has been minimized.

Show comment
Hide comment
@justinmk

justinmk Aug 13, 2017

Member

I'd suggest "new" as the standard verb for creating things. Also in API functions we always spell "window" as win.

nvim_new_floating_win
Member

justinmk commented Aug 13, 2017

I'd suggest "new" as the standard verb for creating things. Also in API functions we always spell "window" as win.

nvim_new_floating_win
@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Aug 13, 2017

Member

new isn't verb though. The alternative would be add, but open signals something more heavy (as a window or channel is) than something you just add to a set like a line or a highlight. create might also work and is slightly more general (like nvim_create_namespace )

Member

bfredl commented Aug 13, 2017

new isn't verb though. The alternative would be add, but open signals something more heavy (as a window or channel is) than something you just add to a set like a line or a highlight. create might also work and is slightly more general (like nvim_create_namespace )

@justinmk

This comment has been minimized.

Show comment
Hide comment
@justinmk

justinmk Aug 13, 2017

Member

new acts like a verb in many programming languages. It has wide recognition, it's short, strong, and familiar.

Member

justinmk commented Aug 13, 2017

new acts like a verb in many programming languages. It has wide recognition, it's short, strong, and familiar.

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Aug 13, 2017

Member

My point was primarily about operation strength, not grammar. Opening/Creating a new window or buffer (or channel) is a lot heavier than adding a new line (we call it [o]pen only because [n] was taken for [n]ext) or highlight, or allocating a new object in "many programming languages". These are all different verbs for a reason. (and if we're discussing common languages, new string() vs string() in javascript is just madness, while in modern c++ well-typed pointers is preferred to new/delete)

Member

bfredl commented Aug 13, 2017

My point was primarily about operation strength, not grammar. Opening/Creating a new window or buffer (or channel) is a lot heavier than adding a new line (we call it [o]pen only because [n] was taken for [n]ext) or highlight, or allocating a new object in "many programming languages". These are all different verbs for a reason. (and if we're discussing common languages, new string() vs string() in javascript is just madness, while in modern c++ well-typed pointers is preferred to new/delete)

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Sep 2, 2017

Member

Added nvim_add_buf(). Note it only adds a buffer to the buffer list, they are not opened/allocated until they are displayed in some window.

let b = nvim_add_buf("")
let w =  nvim_open_floating_window(b,5,10,20,5,0,0)
hi Floating guibg=#000044
call setwinvar(w, '&winhl', 'Normal:Floating')
call setwinvar(w, '&number', 0)
Member

bfredl commented Sep 2, 2017

Added nvim_add_buf(). Note it only adds a buffer to the buffer list, they are not opened/allocated until they are displayed in some window.

let b = nvim_add_buf("")
let w =  nvim_open_floating_window(b,5,10,20,5,0,0)
hi Floating guibg=#000044
call setwinvar(w, '&winhl', 'Normal:Floating')
call setwinvar(w, '&number', 0)
@wsdjeg

This comment has been minimized.

Show comment
Hide comment
@wsdjeg

wsdjeg Sep 2, 2017

Contributor

This PR is awesome, and I have a question about the floating window, I just see the doc what dese Integer x, Integer y, Integer w, Integer h, base on? computer's screen or vim's screen?

Contributor

wsdjeg commented Sep 2, 2017

This PR is awesome, and I have a question about the floating window, I just see the doc what dese Integer x, Integer y, Integer w, Integer h, base on? computer's screen or vim's screen?

Show outdated Hide outdated src/nvim/api/vim.c Outdated
@justinmk

This comment has been minimized.

Show comment
Hide comment
@justinmk

justinmk Sep 2, 2017

Member

nvim_add_buf

Here "new" as the verb makes sense to me: nvim_new_buf(). We're creating an object. The buffer list is like a vtable (an implementation detail) so "add" isn't the characteristic operation.

Member

justinmk commented Sep 2, 2017

nvim_add_buf

Here "new" as the verb makes sense to me: nvim_new_buf(). We're creating an object. The buffer list is like a vtable (an implementation detail) so "add" isn't the characteristic operation.

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Sep 2, 2017

Member

The buffer list most definitely not an implementation detail: it is well known and used vim functionality that a buffer can be "listed" without actually being "opened". The API functions current operation is only to add/return a number in the buffer list, but "add" might not be the best either, as it can return an existing buffer.

However, that is not necessarily the best operation, we could have a function that only creates a new (unnamed) buffer and fully allocates it (this special case might not need to make it temporarily displayed, as there is no swapfile, encoding errors, and probably we want no autocmds either) The current behavior makes sense in the "floating window" case given that it will immediately opened in a window anyway, at which point all the vim stuff happens.

Member

bfredl commented Sep 2, 2017

The buffer list most definitely not an implementation detail: it is well known and used vim functionality that a buffer can be "listed" without actually being "opened". The API functions current operation is only to add/return a number in the buffer list, but "add" might not be the best either, as it can return an existing buffer.

However, that is not necessarily the best operation, we could have a function that only creates a new (unnamed) buffer and fully allocates it (this special case might not need to make it temporarily displayed, as there is no swapfile, encoding errors, and probably we want no autocmds either) The current behavior makes sense in the "floating window" case given that it will immediately opened in a window anyway, at which point all the vim stuff happens.

Show outdated Hide outdated src/nvim/buffer_defs.h Outdated
Show outdated Hide outdated src/nvim/buffer_defs.h Outdated
Show outdated Hide outdated src/nvim/screen.c Outdated
@ZyX-I

This comment has been minimized.

Show comment
Hide comment
@ZyX-I

ZyX-I Sep 2, 2017

Contributor

I have a suggestion: either make x and y Float and allow negative and fractional values, or add another argument which specifies what x and y are related to and what they actually mean. Neither makes sense for TUI, but GUI may open windows outside of the window.

Or do the other thing: open_floating_window already contains lots of arguments. Make that dict:

/// Open floating window
///
/// @param[in]  buffer  Buffer to display in that window.
/// @param[in]  position  Window position, pair of two integers which may be 
/// negative.
/// @param[in]  options  Additional options:
///                      Option | Type | Description
///                      ------ | ---- | -----------
///                      anchor | String | Make position be relative to the 
///                             |        | given anchor: "type-pos" where "type"
///                             |        | is one of "screen", "editor_area",
///                             |        | "wm_window" and "pos" is one of "NW",
///                             |        | "NE", "SW" or "SE"; additionally
///                             |        | "cursor" position is supported.
///                             |        |
///                             |        | Defaults to "cursor".
///                             |        |
///                             |        | TUI only supports "editor_area-…" and
///                             |        | "cursor".
///                      dimensions | pair of Integers | Dimensions in screen
///                                 |                  | cells, width x height.
///                                 |                  |
///                                 |                  | Defaults to either just
///                                 |                  | enough to show the
///                                 |                  | whole buffer contents
///                                 |                  | (not updated when
///                                 |                  | buffer contents
///                                 |                  | changes, call
///                                 |                  | `nvim_reconfigure_floating_window({dimensions: NIL})`
///                                 |                  | then), or just enough
///                                 |                  | to occupy all available
///                                 |                  | space, whichever is
///                                 |                  | lesser.
///                      standalone | Boolean | Make window standalone.
///                                 |         |
///                                 |         | Defaults to false, may be
///                                 |         | ignored in TUI.
///                      focusable | Boolean | Make window not focusable, and
///                                |         | also ignore mouse events.
///                                |         |
///                                |         | Defaults to false.
///                      position_type | String | Position type: either "cells"
///                                    |        | (position in display cells) or
///                                    |        | "pixels".
///                                    |        |
///                                    |        | Defaults to "cells" for
///                                    |        | "editor_area-*" and "cursor"
///                                    |        | anchors, "pixels" otherwise.
Window nvim_open_floating_window(Buffer buffer, ArrayOf(Integer) position,
                                 Dictionary options, Error *const err)

/// Reconfigure floating window
///
/// @param[in]  window  Window to reconfigure.
/// @param[in]  options  @see nvim_open_floating_window()
///                      Missing values mean “do not change option”, use NIL to 
///                      force defaults.
void nvim_reconfigure_floating_window(Window window, Dictionary options,
                                      Error *const err)
Contributor

ZyX-I commented Sep 2, 2017

I have a suggestion: either make x and y Float and allow negative and fractional values, or add another argument which specifies what x and y are related to and what they actually mean. Neither makes sense for TUI, but GUI may open windows outside of the window.

Or do the other thing: open_floating_window already contains lots of arguments. Make that dict:

/// Open floating window
///
/// @param[in]  buffer  Buffer to display in that window.
/// @param[in]  position  Window position, pair of two integers which may be 
/// negative.
/// @param[in]  options  Additional options:
///                      Option | Type | Description
///                      ------ | ---- | -----------
///                      anchor | String | Make position be relative to the 
///                             |        | given anchor: "type-pos" where "type"
///                             |        | is one of "screen", "editor_area",
///                             |        | "wm_window" and "pos" is one of "NW",
///                             |        | "NE", "SW" or "SE"; additionally
///                             |        | "cursor" position is supported.
///                             |        |
///                             |        | Defaults to "cursor".
///                             |        |
///                             |        | TUI only supports "editor_area-…" and
///                             |        | "cursor".
///                      dimensions | pair of Integers | Dimensions in screen
///                                 |                  | cells, width x height.
///                                 |                  |
///                                 |                  | Defaults to either just
///                                 |                  | enough to show the
///                                 |                  | whole buffer contents
///                                 |                  | (not updated when
///                                 |                  | buffer contents
///                                 |                  | changes, call
///                                 |                  | `nvim_reconfigure_floating_window({dimensions: NIL})`
///                                 |                  | then), or just enough
///                                 |                  | to occupy all available
///                                 |                  | space, whichever is
///                                 |                  | lesser.
///                      standalone | Boolean | Make window standalone.
///                                 |         |
///                                 |         | Defaults to false, may be
///                                 |         | ignored in TUI.
///                      focusable | Boolean | Make window not focusable, and
///                                |         | also ignore mouse events.
///                                |         |
///                                |         | Defaults to false.
///                      position_type | String | Position type: either "cells"
///                                    |        | (position in display cells) or
///                                    |        | "pixels".
///                                    |        |
///                                    |        | Defaults to "cells" for
///                                    |        | "editor_area-*" and "cursor"
///                                    |        | anchors, "pixels" otherwise.
Window nvim_open_floating_window(Buffer buffer, ArrayOf(Integer) position,
                                 Dictionary options, Error *const err)

/// Reconfigure floating window
///
/// @param[in]  window  Window to reconfigure.
/// @param[in]  options  @see nvim_open_floating_window()
///                      Missing values mean “do not change option”, use NIL to 
///                      force defaults.
void nvim_reconfigure_floating_window(Window window, Dictionary options,
                                      Error *const err)
@justinmk

This comment has been minimized.

Show comment
Hide comment
@justinmk

justinmk Sep 2, 2017

Member

The buffer list most definitely not an implementation detail: it is well known and used vim functionality

It's an irrelevant detail in the context of the API. When one creates a buffer one does not think "I want to add a buffer to the buffer list", only "I want a new buffer".

Member

justinmk commented Sep 2, 2017

The buffer list most definitely not an implementation detail: it is well known and used vim functionality

It's an irrelevant detail in the context of the API. When one creates a buffer one does not think "I want to add a buffer to the buffer list", only "I want a new buffer".

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Sep 2, 2017

Member

Again, the name describes what the function does right now, which was the simplest thing to implement. The relevant change would be to change what the function actually does (fully allocate a buffer), which will be more involved but probably worth it in the long run.

Member

bfredl commented Sep 2, 2017

Again, the name describes what the function does right now, which was the simplest thing to implement. The relevant change would be to change what the function actually does (fully allocate a buffer), which will be more involved but probably worth it in the long run.

@justinmk justinmk referenced this pull request Oct 31, 2017

Open

[RFC] External ui #5686

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Nov 4, 2017

Member

Update: does not crash on :q<cr>, neither in floating window nor last non-floating window. However the last is guaranteed only if there is no other tabpage. Interaction with multiple tabpages in general is NOT going to be pretty. Especially with external floats which one would expect not to disappear when switching tabpage...

Also, nvim_create_buf works, but modification must be made to nvim_buf_set_option to accept it before buffer is visited.

Current state of art:

let b = nvim_create_buf(v:false)
call nvim_buf_set_option(b, "buftype", "nofile")
call nvim_buf_set_lines(b,0,-1,v:true,["text"])
let w =  nvim_open_floating_win(b,5,10,20,5,0,0)
hi Floating guibg=#000044
call setwinvar(w, '&winhl', 'Normal:Floating')
call setwinvar(w, '&number', 0)
Member

bfredl commented Nov 4, 2017

Update: does not crash on :q<cr>, neither in floating window nor last non-floating window. However the last is guaranteed only if there is no other tabpage. Interaction with multiple tabpages in general is NOT going to be pretty. Especially with external floats which one would expect not to disappear when switching tabpage...

Also, nvim_create_buf works, but modification must be made to nvim_buf_set_option to accept it before buffer is visited.

Current state of art:

let b = nvim_create_buf(v:false)
call nvim_buf_set_option(b, "buftype", "nofile")
call nvim_buf_set_lines(b,0,-1,v:true,["text"])
let w =  nvim_open_floating_win(b,5,10,20,5,0,0)
hi Floating guibg=#000044
call setwinvar(w, '&winhl', 'Normal:Floating')
call setwinvar(w, '&number', 0)

@bfredl bfredl changed the title from [WIP] Floating windows in TUI to [WIP] Floating windows in TUI and Remote UI Nov 11, 2017

@Piping

This comment has been minimized.

Show comment
Hide comment
@Piping

Piping Jul 1, 2018

@bfredl I tried your code! It's awesome!
Is there a way to adding shadow/different color for borders of floating windows?

image

A few enhancement I think of to improve usability is

  1. A floating windows that can be toggled to display/hidden( E.g Ctrl-W Ctrl ~ or wincmd ~)
  2. A border to differentiate floating windows for ground windows
  3. A way to attach floating windows back to ground windows, E.g. Ctrl-W H
  4. adjust window size

Piping commented Jul 1, 2018

@bfredl I tried your code! It's awesome!
Is there a way to adding shadow/different color for borders of floating windows?

image

A few enhancement I think of to improve usability is

  1. A floating windows that can be toggled to display/hidden( E.g Ctrl-W Ctrl ~ or wincmd ~)
  2. A border to differentiate floating windows for ground windows
  3. A way to attach floating windows back to ground windows, E.g. Ctrl-W H
  4. adjust window size
@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Jul 1, 2018

Member

A way to attach floating windows back to ground windows, E.g. Ctrl-W H

This is already possible, use ctrl-W J etc. In general, this first PR will be quite low-level, I hope to add both more useful builtins and runtime functionaly for plugins/users later. For instance, similar to the shared "preview window", there should probably be a shared abstraction for the tooltip/signature window, so a plugin like vim-jedi can show a buffer with the signature with a single or two function calls.

Member

bfredl commented Jul 1, 2018

A way to attach floating windows back to ground windows, E.g. Ctrl-W H

This is already possible, use ctrl-W J etc. In general, this first PR will be quite low-level, I hope to add both more useful builtins and runtime functionaly for plugins/users later. For instance, similar to the shared "preview window", there should probably be a shared abstraction for the tooltip/signature window, so a plugin like vim-jedi can show a buffer with the signature with a single or two function calls.

@chemzqm

This comment has been minimized.

Show comment
Hide comment
@chemzqm

chemzqm Jul 1, 2018

Contributor

This feature would also really useful for workspace symbol search, since the search happens in server, and I can't use existing plugins to do that.

Contributor

chemzqm commented Jul 1, 2018

This feature would also really useful for workspace symbol search, since the search happens in server, and I can't use existing plugins to do that.

@Piping

This comment has been minimized.

Show comment
Hide comment
@Piping

Piping Jul 4, 2018

One crashed case found. junegunn/fzf.vim#664
Otherwise it is awesome and ready for use.

Piping commented Jul 4, 2018

One crashed case found. junegunn/fzf.vim#664
Otherwise it is awesome and ready for use.

@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Jul 7, 2018

Member

@Piping thanks, I will rebase and fix it as soon as I'm done with #8221 (quite soon)

Member

bfredl commented Jul 7, 2018

@Piping thanks, I will rebase and fix it as soon as I'm done with #8221 (quite soon)

@Shougo

This comment has been minimized.

Show comment
Hide comment
@Shougo

Shougo Aug 1, 2018

Contributor

I may use the feature in denite.nvim.

Contributor

Shougo commented Aug 1, 2018

I may use the feature in denite.nvim.

UtkarshMe added a commit to UtkarshMe/neovim that referenced this pull request Aug 1, 2018

tests: Add multigrid test support in screen.lua
Patch by: @bfredl
Note: Remove after neovim#6619 is merged

@Shougo Shougo referenced this pull request Aug 6, 2018

Open

Floating UI support #517

Show outdated Hide outdated runtime/doc/ui.txt Outdated

UtkarshMe added a commit to UtkarshMe/neovim that referenced this pull request Aug 18, 2018

UtkarshMe added a commit to UtkarshMe/neovim that referenced this pull request Sep 12, 2018

tests: Add multigrid test support in screen.lua
Patch by: @bfredl
Note: Remove after neovim#6619 is merged

UtkarshMe added a commit to UtkarshMe/neovim that referenced this pull request Sep 13, 2018

tests: Add multigrid test support in screen.lua
Patch by: @bfredl
Note: Remove after neovim#6619 is merged
@marxin

This comment has been minimized.

Show comment
Hide comment
@marxin

marxin Sep 23, 2018

Thank you very much for working on this. Looks the WIP pull request has been opened for quite some time. Can please anybody explain what's remaining to have in neovim master? Thanks!

marxin commented Sep 23, 2018

Thank you very much for working on this. Looks the WIP pull request has been opened for quite some time. Can please anybody explain what's remaining to have in neovim master? Thanks!

@justinmk

This comment has been minimized.

Show comment
Hide comment
@justinmk

justinmk Sep 23, 2018

Member

@marxin The discussion here is already too long, please avoid asking for status updates. This and related PRs are getting pushed-to regularly, so that should suffice as a status update. If you want to help speed it along, please checkout the PR and test it!

Member

justinmk commented Sep 23, 2018

@marxin The discussion here is already too long, please avoid asking for status updates. This and related PRs are getting pushed-to regularly, so that should suffice as a status update. If you want to help speed it along, please checkout the PR and test it!

UtkarshMe and others added some commits May 19, 2018

api: add nvim_create_buf to create a new emtpy buffer.
NB: loading files into a buffer is non-trivial and requires a window.
Creating an unnamed emtpy buffer is trivial though, thus worth a special
case.
api: use aucmd_prepbuf for nvim_buf_set_option
allows more options to be set on a new, not yet displayed buffer.
floats: implement floating windows
simple implementation that is unreliable for TUI (improved in later
commit)

Co-Author: Dongdong Zhou <dzhou121@gmail.com>
floats: handle switching tabs
standalone floats are always visible
inline floats gets shown and hidden as necessary
@bfredl

This comment has been minimized.

Show comment
Hide comment
@bfredl

bfredl Oct 18, 2018

Member

@marxin I just rebased this on top of #8455. There is some remaining rough edges to sort out, like making the external UI API for both these features more consistent, as well as both working correctly with tabs and multiline messages. I think it would be reasonable to get #8455 and this PR merged soon after 0.3.2 is released, with the understanding that these features remain experimental for a while during the 0.3.3 cycle.

Member

bfredl commented Oct 18, 2018

@marxin I just rebased this on top of #8455. There is some remaining rough edges to sort out, like making the external UI API for both these features more consistent, as well as both working correctly with tabs and multiline messages. I think it would be reasonable to get #8455 and this PR merged soon after 0.3.2 is released, with the understanding that these features remain experimental for a while during the 0.3.3 cycle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment