Skip to content

Commit 03c1ba7

Browse files
committed
fix(popup): correct image rendering position in floating windows
Changes: - use screenpos for floating window position to handle Neovim's auto-repositioning - only subtract status line offset from bounds for normal windows, not floating - defer rendering with 10ms delay to ensure window position is finalized - fix viewport detection to work correctly for floating windows - remove incorrect border check for normal windows (only floating can have borders)
1 parent 7ad6343 commit 03c1ba7

File tree

3 files changed

+78
-46
lines changed

3 files changed

+78
-46
lines changed

lua/image/renderer.lua

Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -119,20 +119,28 @@ local render = function(image)
119119
return false
120120
end
121121

122-
-- global offsets
123-
local global_offsets = utils.offsets.get_global_offsets(window.id)
124122
-- window bounds
125123
bounds = window.rect
126-
bounds.bottom = bounds.bottom - 1
124+
-- subtract 1 for normal windows, not floating windows
125+
-- TODO: do we even still need this?
126+
if not window.is_floating then bounds.bottom = bounds.bottom - 1 end
127127

128-
-- this is ugly, and if get_global_offsets() is changed this could break
129-
bounds.top = bounds.top + global_offsets.y
130-
bounds.bottom = bounds.bottom + global_offsets.y
131-
bounds.left = bounds.left + global_offsets.x
132-
bounds.right = bounds.right
128+
-- only apply global offsets to non-floating windows
129+
if not window.is_floating then
130+
-- global offsets
131+
local global_offsets = utils.offsets.get_global_offsets(window.id)
133132

134-
if utils.offsets.get_border_shape(window.id).left > 0 then
135-
bounds.right = bounds.right + 1 --
133+
log.debug(" Applying global offsets: " .. vim.inspect(global_offsets))
134+
135+
-- this is ugly, and if get_global_offsets() is changed this could break
136+
bounds.top = bounds.top + global_offsets.y
137+
bounds.bottom = bounds.bottom + global_offsets.y
138+
bounds.left = bounds.left + global_offsets.x
139+
bounds.right = bounds.right
140+
141+
log.debug(" Bounds after offsets: " .. vim.inspect(bounds))
142+
else
143+
log.debug(" Floating window - NOT applying global offsets")
136144
end
137145

138146
local max_width_window_percentage = --
@@ -144,20 +152,28 @@ local render = function(image)
144152
or state.options.max_height_window_percentage
145153

146154
if not image.ignore_global_max_size then
155+
local offset_x = 0
156+
local offset_y = 0
157+
if not window.is_floating then
158+
local global_offsets = utils.offsets.get_global_offsets(window.id)
159+
offset_x = global_offsets.x
160+
offset_y = global_offsets.y
161+
end
162+
147163
if type(max_width_window_percentage) == "number" then
148164
width = math.min(
149165
-- original
150166
width,
151167
-- max_window_percentage
152-
math.floor((window.width - global_offsets.x) * max_width_window_percentage / 100)
168+
math.floor((window.width - offset_x) * max_width_window_percentage / 100)
153169
)
154170
end
155171
if type(max_height_window_percentage) == "number" then
156172
height = math.min(
157173
-- original
158174
height,
159175
-- max_window_percentage
160-
math.floor((window.height - global_offsets.y) * max_height_window_percentage / 100)
176+
math.floor((window.height - offset_y) * max_height_window_percentage / 100)
161177
)
162178
end
163179
end
@@ -183,10 +199,33 @@ local render = function(image)
183199
absolute_y = absolute_y + image.render_offset_top
184200
end
185201
else
202+
-- get window object
203+
local window = nil
204+
if image.window then
205+
window = utils.window.get_window(image.window, {
206+
with_masks = state.options.window_overlap_clear_enabled,
207+
ignore_masking_filetypes = state.options.window_overlap_clear_ft_ignore,
208+
})
209+
end
210+
186211
local win_info = vim.fn.getwininfo(image.window)[1]
187-
local screen_pos = vim.fn.screenpos(image.window, math.max(1, original_y), original_x + 1)
212+
local win_config = vim.api.nvim_win_get_config(image.window)
188213

214+
local screen_pos
189215
local is_partial_scroll = false
216+
217+
-- calculate screen position based on window type
218+
if window and window.is_floating then
219+
-- for floating windows, the position is relative to the window's content area
220+
screen_pos = {
221+
row = window.rect.top + original_y + 1,
222+
col = window.rect.left + original_x + 1,
223+
}
224+
else
225+
-- for normal windows, we call screenpos
226+
screen_pos = vim.fn.screenpos(image.window, math.max(1, original_y), original_x + 1)
227+
end
228+
190229
if
191230
screen_pos.col == 0 --
192231
and screen_pos.row == 0 --
@@ -278,10 +317,8 @@ local render = function(image)
278317
absolute_y = screen_pos.row
279318
end
280319
-- apply render_offset_top offset if set (but not for floating windows and not during partial scroll)
281-
local is_floating = vim.api.nvim_win_get_config(image.window).relative ~= ""
282-
if is_floating and original_y == 0 then
283-
absolute_y = absolute_y - 1
284-
elseif image.render_offset_top and image.render_offset_top > 0 and not is_floating and not is_partial_scroll then
320+
local is_floating = window and window.is_floating or false
321+
if image.render_offset_top and image.render_offset_top > 0 and not is_floating and not is_partial_scroll then
285322
absolute_y = absolute_y + image.render_offset_top
286323
end
287324
end
@@ -293,23 +330,6 @@ local render = function(image)
293330
local is_left = absolute_x + width <= bounds.left
294331
local is_right = absolute_x >= bounds.right
295332

296-
log.debug(("bounds check for %s"):format(image.id), {
297-
absolute_y = absolute_y,
298-
height = height,
299-
bounds_top = bounds.top,
300-
bounds_bottom = bounds.bottom,
301-
laststatus_offset = laststatus_offset,
302-
is_above = is_above,
303-
is_below = is_below,
304-
is_left = is_left,
305-
is_right = is_right,
306-
is_rendered = image.is_rendered,
307-
checks = {
308-
above = string.format("%d + %d <= %d", absolute_y, height, bounds.top),
309-
below = string.format("%d > %d + %d", absolute_y, bounds.bottom, laststatus_offset),
310-
},
311-
})
312-
313333
if is_above or is_below or is_left or is_right then
314334
if image.is_rendered then
315335
log.debug(("CLEARING out of bounds image %s"):format(image.id))
@@ -359,12 +379,6 @@ local render = function(image)
359379
if absolute_y + height > bounds.bottom then
360380
cropped_pixel_height = (bounds.bottom - absolute_y + 1) * term_size.cell_height
361381
needs_crop = true
362-
log.debug(("Image %s crop bottom"):format(image.id), {
363-
absolute_y = absolute_y,
364-
height = height,
365-
bounds_bottom = bounds.bottom,
366-
cropped_pixel_height = cropped_pixel_height,
367-
})
368382
end
369383

370384
-- compute resize

lua/image/utils/document.lua

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ local create_document_integration = function(config)
139139
image.window = win
140140
image.buffer = buf
141141

142-
vim.schedule(function()
142+
-- render after window is open
143+
vim.defer_fn(function()
143144
if vim.api.nvim_win_is_valid(win) then
144145
local win_info = vim.fn.getwininfo(win)[1]
145146
if win_info and win_info.wincol > 0 then
@@ -151,7 +152,7 @@ local create_document_integration = function(config)
151152
})
152153
end
153154
end
154-
end)
155+
end, 10)
155156
-- close the floating window when the cursor moves
156157
vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, {
157158
callback = function()

lua/image/utils/window.lua

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
local offsets = require("image/utils/offsets")
2+
13
---@param opts { normal: boolean, floating: boolean, with_masks: boolean, ignore_masking_filetypes: string[] }
24
---@return Window[]
35
local get_windows = function(opts)
@@ -15,6 +17,21 @@ local get_windows = function(opts)
1517
local scroll_y = tonumber(vim.fn.win_execute(id, "echo line('w0')")) - 1
1618
local is_visible = true
1719

20+
local rect_top, rect_left
21+
local content_width = columns
22+
local content_height = rows
23+
24+
if config.relative ~= "" then
25+
-- floating
26+
local screen_pos = vim.fn.screenpos(id, 1, 1)
27+
rect_top = screen_pos.row - 2
28+
rect_left = screen_pos.col - 1
29+
else
30+
-- normal
31+
rect_top = pos[1]
32+
rect_left = pos[2]
33+
end
34+
1835
local window = {
1936
id = id,
2037
buffer = buffer,
@@ -31,10 +48,10 @@ local get_windows = function(opts)
3148
is_floating = config.relative ~= "",
3249
zindex = config.zindex or 0,
3350
rect = {
34-
top = pos[1],
35-
right = pos[2] + columns,
36-
bottom = pos[1] + rows - (vim.o.laststatus == 2 and 1 or 0),
37-
left = pos[2],
51+
top = rect_top,
52+
right = rect_left + content_width,
53+
bottom = rect_top + content_height - (config.relative == "" and vim.o.laststatus == 2 and 1 or 0),
54+
left = rect_left,
3855
},
3956
masks = {},
4057
}

0 commit comments

Comments
 (0)