MunifTanjim / nui.nvim

UI Component Library for Neovim.
MIT License
1.62k stars 57 forks source link

Evenly spaced splits in box layout #253

Closed rcarriga closed 1 year ago

rcarriga commented 1 year ago

Hey there, I'm looking at using nui.nvim within nvim-dap-ui as it seems like it'd offer more flexibility to users and also allow me to reduce the size of the code :smile: I'm trying to figure out how to replicate the default config which has 4 windows to the left and 2 windows at the bottom

image

Right now I've got this

local Layout = require("nui.layout")
local Split = require("nui.split")

local cur_win = vim.api.nvim_get_current_win()
local col_layout = Layout(
  {
    position = "left",
    relative = "editor",
    size = "20%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
  }, {
    dir = "col",
  })
)

col_layout:mount()

vim.api.nvim_set_current_win(cur_win)

local row_layout = Layout(
  {
    position = "bottom",
    relative = "editor",
    size = "25%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "50%" }),
    Layout.Box(Split({}), { size = "50%" }),
  }, {
    dir = "row",
  })
)

row_layout:mount()

but this results in the windows not being evenly spaced image

Is there something I'm doing wrong?

MunifTanjim commented 1 year ago

Hey @rcarriga , can you check if https://github.com/MunifTanjim/nui.nvim/pull/254 improves the situation?

Also try to mount the row_layout first. Or if you want to mount col_layout first, try using relative = "win" for row_layout.

local Layout = require("nui.layout")
local Split = require("nui.split")

local cur_win = vim.api.nvim_get_current_win()

local row_layout = Layout(
  {
    position = "bottom",
    relative = "editor",
    size = "25%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "50%" }),
    Layout.Box(Split({}), { size = "50%" }),
  }, {
    dir = "row",
  })
)

row_layout:mount()
vim.api.nvim_set_current_win(cur_win)

local col_layout = Layout(
  {
    position = "left",
    relative = "editor",
    size = "20%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
  }, {
    dir = "col",
  })
)

col_layout:mount()
vim.api.nvim_set_current_win(cur_win)
rcarriga commented 1 year ago

Thanks for the quick response! The column layout now looks perfect :smile:

However the row layout seems to not adjust to the column layout being opened and so the window on the left is smaller than the right image

I tried calling row_layout:update() (which is similar to how I solved the same issue in nvim-dap-ui) but it seems to make things go a bit wrong :sweat_smile: image

I also tried playing around with the opening order and relative settings but had no luck. Any ideas on how I can make the windows evenly sized at the bottom?

MunifTanjim commented 1 year ago

I tried calling row_layout:update() (which is similar to how I solved the same issue in nvim-dap-ui) but it seems to make things go a bit wrong 😅

I'll look into the :update() issue.


In the meantime, can you try this?

local Layout = require("nui.layout")
local Split = require("nui.split")

local cur_win = vim.api.nvim_get_current_win()

local col_layout = Layout(
  {
    position = "left",
    relative = "editor",
    size = "20%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
  }, {
    dir = "col",
  })
)

col_layout:mount()
vim.api.nvim_set_current_win(cur_win)

local row_layout = Layout(
  {
    position = "bottom",
    relative = "win", -- makes it (size and position) relative to the focused window
    size = "25%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "50%" }),
    Layout.Box(Split({}), { size = "50%" }),
  }, {
    dir = "row",
  })
)

row_layout:mount()
vim.api.nvim_set_current_win(cur_win)
rcarriga commented 1 year ago

That works if there's only one window open but with multiple windows, I believe I'll need relative = "editor" image

Updated version of my last script to handle going to the first window before opening the layouts (still has the issue with unevenly spaced row_layout windows but can be in any window when opening)

local Layout = require("nui.layout")
local Split = require("nui.split")

local cur_win = vim.api.nvim_get_current_win()
local first_win = vim.fn.win_getid(1)
vim.api.nvim_set_current_win(first_win)

local col_layout = Layout(
  {
    position = "left",
    relative = "editor",
    size = "20%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
    Layout.Box(Split({}), { size = "25%" }),
  }, {
    dir = "col",
  })
)

local row_layout = Layout(
  {
    position = "bottom",
    relative = "editor",
    size = "25%",
  },
  Layout.Box({
    Layout.Box(Split({}), { size = "50%" }),
    Layout.Box(Split({}), { size = "50%" }),
  }, {
    dir = "row",
  })
)

row_layout:mount()
vim.api.nvim_set_current_win(first_win)
col_layout:mount()
vim.api.nvim_set_current_win(cur_win)

image

MunifTanjim commented 1 year ago

Got it. Since 20% of editor width is occupied by col_layout, try using 40% and 40% for row_layout splits?

MunifTanjim commented 1 year ago

by the way, how do you handle the scenario where a new file is opened in a split when nvim-dap-ui is already visible? 🤔

rcarriga commented 1 year ago

Got it. Since 20% of editor width is occupied by col_layout, try using 40% and 40% for row_layout splits?

Ah that's it so all sizes are relative to the editor, I'd assumed that it was only the parent layout size and the children would be relative to the layout

by the way, how do you handle the scenario where a new file is opened in a split when nvim-dap-ui is already visible? thinking

I set winfixwidth and winfixheight but it looks like nui prevents them from being set for some reason. Is there a way to set them without breaking nui?

MunifTanjim commented 1 year ago

Ah that's it so all sizes are relative to the editor, I'd assumed that it was only the parent layout size and the children would be relative to the layout

For Layout with floats, that is the case. Because Layout opens a floating window for itself, and then places the children floating windows on top of that window.

But for Layout with splits, we can't do that. All the splits are positioned side by side... there's no actual parent. That's why the calculation is a bit different.

MunifTanjim commented 1 year ago

I set winfixwidth and winfixheight but it looks like nui prevents them from being set for some reason. Is there a way to set them without breaking nui?

After https://github.com/MunifTanjim/nui.nvim/pull/256 is merged, you can do this:

local win_options = {
  winfixwidth = true,
  winfixheight = true,
}

Layout.Box(Split({ win_options = win_options }), { size = "40%" })
MunifTanjim commented 1 year ago

Can you try this with the latest main branch now?

local Layout = require("nui.layout")
local Split = require("nui.split")

local cur_win = vim.api.nvim_get_current_win()

local win_options = {
  winfixwidth = true,
  winfixheight = true,
}

local rl = Layout(
  {
    position = "bottom",
    relative = "editor",
    size = "25%",
  },
  Layout.Box({
    Layout.Box(Split({ win_options = win_options }), { size = "40%" }),
    Layout.Box(Split({ win_options = win_options }), { size = "40%" }),
  }, {
    dir = "row",
  })
)

vim.api.nvim_set_current_win(cur_win)
rl:mount()

local cl = Layout(
  {
    position = "left",
    relative = "editor",
    size = "20%",
  },
  Layout.Box({
    Layout.Box(Split({ win_options = win_options }), { size = "25%" }),
    Layout.Box(Split({ win_options = win_options }), { size = "25%" }),
    Layout.Box(Split({ win_options = win_options }), { size = "25%" }),
    Layout.Box(Split({ win_options = win_options }), { size = "25%" }),
  }, {
    dir = "col",
  })
)

vim.api.nvim_set_current_win(cur_win)
cl:mount()

Also thanks for experimenting with it and pointig out these bugs.

rcarriga commented 1 year ago

That seems to be working perfectly thank you, especially for the quick fixes! Happy to experiment, dapui was a pain to implement and has plenty of issues, which is why I'd rather migrate to nui :sweat_smile: I'll be sure to report any more issues