mirror of
https://github.com/neovim/neovim.git
synced 2026-05-01 11:34:56 +00:00
fix(ui): apply 'pumborder' to mouse menu, fix overflow #36193
Problem: Mouse popup menus (right-click context menus) do not respect the 'pumborder' option and could overflow screen boundaries when borders were enabled near the edge. Solution: - Remove the mouse menu exclusion from border rendering. - Add boundary check to shift menu left when border would exceed screen width, ensuring complete visibility of menu content and borders.
This commit is contained in:
@@ -4903,6 +4903,9 @@ A jump table for the options with a short description can be found at |Q_op|.
|
|||||||
valid values. |hl-PmenuBorder| is used for highlighting the border, and when
|
valid values. |hl-PmenuBorder| is used for highlighting the border, and when
|
||||||
style is "shadow" the |hl-PmenuShadow| and |hl-PmenuShadowThrough| groups are used.
|
style is "shadow" the |hl-PmenuShadow| and |hl-PmenuShadowThrough| groups are used.
|
||||||
|
|
||||||
|
This option also applies to mouse popup menus when 'mousemodel' is set to
|
||||||
|
"popup" or "popup_setpos", which will display borders using the same style.
|
||||||
|
|
||||||
*'pumheight'* *'ph'*
|
*'pumheight'* *'ph'*
|
||||||
'pumheight' 'ph' number (default 0)
|
'pumheight' 'ph' number (default 0)
|
||||||
global
|
global
|
||||||
|
|||||||
3
runtime/lua/vim/_meta/options.lua
generated
3
runtime/lua/vim/_meta/options.lua
generated
@@ -5108,6 +5108,9 @@ vim.go.pb = vim.go.pumblend
|
|||||||
--- valid values. `hl-PmenuBorder` is used for highlighting the border, and when
|
--- valid values. `hl-PmenuBorder` is used for highlighting the border, and when
|
||||||
--- style is "shadow" the `hl-PmenuShadow` and `hl-PmenuShadowThrough` groups are used.
|
--- style is "shadow" the `hl-PmenuShadow` and `hl-PmenuShadowThrough` groups are used.
|
||||||
---
|
---
|
||||||
|
--- This option also applies to mouse popup menus when 'mousemodel' is set to
|
||||||
|
--- "popup" or "popup_setpos", which will display borders using the same style.
|
||||||
|
---
|
||||||
--- @type string
|
--- @type string
|
||||||
vim.o.pumborder = ""
|
vim.o.pumborder = ""
|
||||||
vim.go.pumborder = vim.o.pumborder
|
vim.go.pumborder = vim.o.pumborder
|
||||||
|
|||||||
@@ -6699,6 +6699,9 @@ local options = {
|
|||||||
Defines the default border style of popupmenu windows. See 'winborder' for
|
Defines the default border style of popupmenu windows. See 'winborder' for
|
||||||
valid values. |hl-PmenuBorder| is used for highlighting the border, and when
|
valid values. |hl-PmenuBorder| is used for highlighting the border, and when
|
||||||
style is "shadow" the |hl-PmenuShadow| and |hl-PmenuShadowThrough| groups are used.
|
style is "shadow" the |hl-PmenuShadow| and |hl-PmenuShadowThrough| groups are used.
|
||||||
|
|
||||||
|
This option also applies to mouse popup menus when 'mousemodel' is set to
|
||||||
|
"popup" or "popup_setpos", which will display borders using the same style.
|
||||||
]=],
|
]=],
|
||||||
short_desc = N_('border of popupmenu'),
|
short_desc = N_('border of popupmenu'),
|
||||||
type = 'string',
|
type = 'string',
|
||||||
|
|||||||
@@ -679,10 +679,7 @@ void pum_redraw(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int scroll_range = pum_size - pum_height;
|
int scroll_range = pum_size - pum_height;
|
||||||
|
if (fconfig.border) {
|
||||||
// avoid set border for mouse menu
|
|
||||||
int mouse_menu = State != MODE_CMDLINE && pum_grid.zindex == kZIndexCmdlinePopupMenu;
|
|
||||||
if (!mouse_menu && fconfig.border) {
|
|
||||||
grid_draw_border(&pum_grid, &fconfig, NULL, 0, NULL);
|
grid_draw_border(&pum_grid, &fconfig, NULL, 0, NULL);
|
||||||
if (!fconfig.shadow) {
|
if (!fconfig.shadow) {
|
||||||
row++;
|
row++;
|
||||||
@@ -1437,18 +1434,21 @@ static void pum_position_at_mouse(int min_width)
|
|||||||
pum_anchor_grid = grid;
|
pum_anchor_grid = grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_row - row > pum_size || max_row - row > row - min_row) {
|
// Both width and height are 1 for shadow border, otherwise 2
|
||||||
|
int border_width = pum_border_width();
|
||||||
|
int border_height = border_width;
|
||||||
|
if (max_row - row > pum_size + border_height || max_row - row > row - min_row) {
|
||||||
// Enough space below the mouse row,
|
// Enough space below the mouse row,
|
||||||
// or there is more space below the mouse row than above.
|
// or there is more space below the mouse row than above.
|
||||||
pum_above = false;
|
pum_above = false;
|
||||||
pum_row = row + 1;
|
pum_row = row + 1;
|
||||||
if (pum_height > max_row - pum_row) {
|
if (pum_height + border_height > max_row - pum_row) {
|
||||||
pum_height = max_row - pum_row;
|
pum_height = max_row - pum_row - border_height;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Show above the mouse row, reduce height if it does not fit.
|
// Show above the mouse row, reduce height if it does not fit.
|
||||||
pum_above = true;
|
pum_above = true;
|
||||||
pum_row = row - pum_size;
|
pum_row = row - pum_size - border_height;
|
||||||
if (pum_row < min_row) {
|
if (pum_row < min_row) {
|
||||||
pum_height += pum_row - min_row;
|
pum_height += pum_row - min_row;
|
||||||
pum_row = min_row;
|
pum_row = min_row;
|
||||||
@@ -1456,25 +1456,25 @@ static void pum_position_at_mouse(int min_width)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pum_rl) {
|
if (pum_rl) {
|
||||||
if (col - min_col + 1 >= pum_base_width
|
if (col - min_col + 1 >= pum_base_width + border_width
|
||||||
|| col - min_col + 1 > min_width) {
|
|| col - min_col + 1 > min_width + border_width) {
|
||||||
// Enough space to show at mouse column.
|
// Enough space to show at mouse column.
|
||||||
pum_col = col;
|
pum_col = col;
|
||||||
} else {
|
} else {
|
||||||
// Not enough space, left align with window.
|
// Not enough space, left align with window.
|
||||||
pum_col = min_col + MIN(pum_base_width, min_width) - 1;
|
pum_col = min_col + MIN(pum_base_width + border_width, min_width + border_width) - 1;
|
||||||
}
|
}
|
||||||
pum_width = pum_col - min_col + 1;
|
pum_width = pum_col - min_col + 1 - border_width;
|
||||||
} else {
|
} else {
|
||||||
if (max_col - col >= pum_base_width
|
if (max_col - col >= pum_base_width + border_width
|
||||||
|| max_col - col > min_width) {
|
|| max_col - col > min_width + border_width) {
|
||||||
// Enough space to show at mouse column.
|
// Enough space to show at mouse column.
|
||||||
pum_col = col;
|
pum_col = col;
|
||||||
} else {
|
} else {
|
||||||
// Not enough space, right align with window.
|
// Not enough space, right align with window.
|
||||||
pum_col = max_col - MIN(pum_base_width, min_width);
|
pum_col = max_col - MIN(pum_base_width + border_width, min_width + border_width);
|
||||||
}
|
}
|
||||||
pum_width = max_col - pum_col;
|
pum_width = max_col - pum_col - border_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
pum_width = MIN(pum_width, pum_base_width + 1);
|
pum_width = MIN(pum_width, pum_base_width + 1);
|
||||||
@@ -1492,7 +1492,10 @@ static void pum_select_mouse_pos(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (grid == pum_grid.handle) {
|
if (grid == pum_grid.handle) {
|
||||||
pum_selected = row;
|
// Offset by 1 when border width is 2 (non-shadow border)
|
||||||
|
int border_offset = pum_border_width() == 2 ? 1 : 0;
|
||||||
|
int item = row - border_offset;
|
||||||
|
pum_selected = (item >= 0 && item < pum_height) ? item : -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9398,6 +9398,146 @@ describe('builtin popupmenu', function()
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
it("'pumborder' on mouse-menu displays completely within screen", function()
|
||||||
|
screen:try_resize(40, 12)
|
||||||
|
command('set pumborder=rounded')
|
||||||
|
-- Click near right edge to test boundary handling
|
||||||
|
if send_mouse_grid then
|
||||||
|
api.nvim_input_mouse('right', 'press', '', 2, 2, 35)
|
||||||
|
else
|
||||||
|
feed('<RightMouse><35,2>')
|
||||||
|
end
|
||||||
|
if multigrid then
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
## grid 1
|
||||||
|
[2:----------------------------------------]|*11
|
||||||
|
[3:----------------------------------------]|
|
||||||
|
## grid 2
|
||||||
|
^ |
|
||||||
|
{1:~ }|*10
|
||||||
|
## grid 3
|
||||||
|
|
|
||||||
|
## grid 4
|
||||||
|
{n:╭─────────────────────╮}|
|
||||||
|
{n:│ Inspect │}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ Paste │}|
|
||||||
|
{n:│ Select All │}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ How-to disable mouse│}|
|
||||||
|
{n:╰─────────────────────╯}|
|
||||||
|
]],
|
||||||
|
float_pos = {
|
||||||
|
[4] = { -1, 'NW', 2, 3, 17, false, 250, 2, 3, 17 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*2
|
||||||
|
{1:~ }{n:╭─────────────────────╮}|
|
||||||
|
{1:~ }{n:│ Inspect │}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ Paste │}|
|
||||||
|
{1:~ }{n:│ Select All │}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ How-to disable mouse│}|
|
||||||
|
{1:~ }{n:╰─────────────────────╯}|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
if send_mouse_grid then
|
||||||
|
api.nvim_input_mouse('move', '', '', 4, 1, 1)
|
||||||
|
else
|
||||||
|
feed('<MouseMove><25,4>')
|
||||||
|
end
|
||||||
|
if multigrid then
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
## grid 1
|
||||||
|
[2:----------------------------------------]|*11
|
||||||
|
[3:----------------------------------------]|
|
||||||
|
## grid 2
|
||||||
|
^ |
|
||||||
|
{1:~ }|*10
|
||||||
|
## grid 3
|
||||||
|
|
|
||||||
|
## grid 4
|
||||||
|
{n:╭─────────────────────╮}|
|
||||||
|
{n:│}{12: Inspect }{n:│}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ Paste │}|
|
||||||
|
{n:│ Select All │}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ How-to disable mouse│}|
|
||||||
|
{n:╰─────────────────────╯}|
|
||||||
|
]],
|
||||||
|
float_pos = {
|
||||||
|
[4] = { -1, 'NW', 2, 3, 17, false, 250, 2, 3, 17 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else
|
||||||
|
screen:expect([[
|
||||||
|
^ |
|
||||||
|
{1:~ }|*2
|
||||||
|
{1:~ }{n:╭─────────────────────╮}|
|
||||||
|
{1:~ }{n:│}{12: Inspect }{n:│}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ Paste │}|
|
||||||
|
{1:~ }{n:│ Select All │}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ How-to disable mouse│}|
|
||||||
|
{1:~ }{n:╰─────────────────────╯}|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
-- when right-clicking on the bottom menu, it should appear above the mouse_row
|
||||||
|
if send_mouse_grid then
|
||||||
|
api.nvim_input_mouse('right', 'press', '', 2, 8, 20)
|
||||||
|
else
|
||||||
|
feed('<RightMouse><20,8>')
|
||||||
|
end
|
||||||
|
if multigrid then
|
||||||
|
screen:expect({
|
||||||
|
grid = [[
|
||||||
|
## grid 1
|
||||||
|
[2:----------------------------------------]|*11
|
||||||
|
[3:----------------------------------------]|
|
||||||
|
## grid 2
|
||||||
|
^ |
|
||||||
|
{1:~ }|*10
|
||||||
|
## grid 3
|
||||||
|
|
|
||||||
|
## grid 4
|
||||||
|
{n:╭─────────────────────╮}|
|
||||||
|
{n:│ Inspect │}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ Paste │}|
|
||||||
|
{n:│ Select All │}|
|
||||||
|
{n:│ │}|
|
||||||
|
{n:│ How-to disable mouse│}|
|
||||||
|
{n:╰─────────────────────╯}|
|
||||||
|
]],
|
||||||
|
float_pos = {
|
||||||
|
[4] = { -1, 'SW', 2, 6, 17, false, 250, 2, 0, 17 },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
else
|
||||||
|
screen:expect([[
|
||||||
|
^ {n:╭─────────────────────╮}|
|
||||||
|
{1:~ }{n:│ Inspect │}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ Paste │}|
|
||||||
|
{1:~ }{n:│ Select All │}|
|
||||||
|
{1:~ }{n:│ │}|
|
||||||
|
{1:~ }{n:│ How-to disable mouse│}|
|
||||||
|
{1:~ }{n:╰─────────────────────╯}|
|
||||||
|
{1:~ }|*3
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe('with ext_multigrid and actual mouse grid', function()
|
describe('with ext_multigrid and actual mouse grid', function()
|
||||||
|
|||||||
Reference in New Issue
Block a user