int i;
CHECK_LIST_MATERIALIZE(list);
- for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
- ++i, li = li->li_next)
+ wp->w_border_highlight_isset = TRUE;
+ // Clear all highlights if list is empty
+ if (list->lv_len == 0)
{
- str = tv_get_string(&li->li_tv);
- if (*str != NUL)
+ for (i = 0; i < 4; ++i)
{
vim_free(wp->w_border_highlight[i]);
- wp->w_border_highlight[i] = vim_strsave(str);
+ wp->w_border_highlight[i] = NULL;
}
}
- if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
- for (i = 1; i < 4; ++i)
+ else
+ {
+ for (i = 0, li = list->lv_first; i < 4 && i < list->lv_len;
+ ++i, li = li->li_next)
{
- vim_free(wp->w_border_highlight[i]);
- wp->w_border_highlight[i] =
- vim_strsave(wp->w_border_highlight[0]);
+ str = tv_get_string(&li->li_tv);
+ if (*str != NUL)
+ {
+ vim_free(wp->w_border_highlight[i]);
+ wp->w_border_highlight[i] = vim_strsave(str);
+ }
}
+ if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
+ for (i = 1; i < 4; ++i)
+ {
+ vim_free(wp->w_border_highlight[i]);
+ wp->w_border_highlight[i] =
+ vim_strsave(wp->w_border_highlight[0]);
+ }
+ }
}
}
wp->w_redr_type = UPD_NOT_VALID;
}
+/*
+ * Return TRUE if any visible popup window needs a redraw.
+ */
+ int
+popup_need_redraw(void)
+{
+ win_T *wp;
+
+ FOR_ALL_POPUPWINS(wp)
+ if ((wp->w_popup_flags & POPF_HIDDEN) == 0 && wp->w_redr_type != 0)
+ return TRUE;
+ FOR_ALL_POPUPWINS_IN_TAB(curtab, wp)
+ if ((wp->w_popup_flags & POPF_HIDDEN) == 0 && wp->w_redr_type != 0)
+ return TRUE;
+ return FALSE;
+}
+
/*
* Set the color for a notification window.
*/
dict_T *dict;
int id;
win_T *wp;
+ int old_winrow;
+ int old_wincol;
+ int old_height;
+ int old_width;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
return;
dict = argvars[1].vval.v_dict;
+ // Save old position for redrawing
+ old_winrow = wp->w_winrow;
+ old_wincol = wp->w_wincol;
+ old_height = wp->w_height;
+ old_width = wp->w_width;
+
apply_move_options(wp, dict);
if (wp->w_winrow + wp->w_height >= cmdline_row)
clear_cmdline = TRUE;
popup_adjust_position(wp);
+
+ // Redraw the old position to clear ghost images
+ if (old_winrow != wp->w_winrow || old_wincol != wp->w_wincol
+ || old_height != wp->w_height || old_width != wp->w_width)
+ redraw_all_later(UPD_NOT_VALID);
}
/*
#ifdef FEAT_PROP_POPUP
int old_blend;
#endif
+ int old_zindex;
+ int old_popup_flags;
+ char_u *old_scrollbar_highlight;
+ char_u *old_thumb_highlight;
+ char_u *old_border_highlight[4];
+ int need_redraw = FALSE;
+ int i;
if (in_vim9script()
&& (check_for_number_arg(argvars, 0) == FAIL
#ifdef FEAT_PROP_POPUP
old_blend = wp->w_popup_blend;
#endif
+ old_zindex = wp->w_zindex;
+ old_popup_flags = wp->w_popup_flags;
+ old_scrollbar_highlight = wp->w_scrollbar_highlight;
+ old_thumb_highlight = wp->w_thumb_highlight;
+ for (i = 0; i < 4; i++)
+ old_border_highlight[i] = wp->w_border_highlight[i];
(void)apply_options(wp, dict, FALSE);
+ // Check if visual options changed and redraw if needed
if (old_firstline != wp->w_firstline)
+ need_redraw = TRUE;
+ if (old_zindex != wp->w_zindex)
+ need_redraw = TRUE;
+ if (old_popup_flags != wp->w_popup_flags)
+ need_redraw = TRUE;
+ if (old_scrollbar_highlight != wp->w_scrollbar_highlight)
+ need_redraw = TRUE;
+ if (old_thumb_highlight != wp->w_thumb_highlight)
+ need_redraw = TRUE;
+ for (i = 0; i < 4; i++)
+ if (old_border_highlight[i] != wp->w_border_highlight[i])
+ {
+ need_redraw = TRUE;
+ break;
+ }
+
+ if (need_redraw)
redraw_win_later(wp, UPD_NOT_VALID);
#ifdef FEAT_PROP_POPUP
// Force redraw if opacity value changed
for (i = 0; i < 4; ++i)
if (wp->w_border_highlight[i] != NULL)
break;
- if (i == 4)
+ // Only include "borderhighlight" if it was explicitly set (even if empty)
+ // or if at least one highlight is set.
+ if (i == 4 && !wp->w_border_highlight_isset)
return;
list = list_alloc();
return;
dict_add_list(dict, "borderhighlight", list);
+ // When all highlights are NULL (cleared to empty list), return empty list.
+ if (i == 4)
+ return;
for (i = 0; i < 4; ++i)
list_append_string(list, wp->w_border_highlight[i], -1);
}
popup_adjust_position(curwin);
}
+#ifdef FEAT_PROP_POPUP
+static schar_T *base_screenlines = NULL;
+static int *base_screenattrs = NULL;
+static u8char_T *base_screenlinesuc = NULL;
+static int base_screen_rows = 0;
+static int base_screen_cols = 0;
+
+/*
+ * Get the base screen cell saved before drawing opacity popups.
+ * Returns TRUE if the cell is available.
+ */
+ int
+popup_get_base_screen_cell(int row, int col, schar_T *linep, int *attrp,
+ u8char_T *ucp)
+{
+ if (base_screenlines == NULL || base_screenattrs == NULL)
+ return FALSE;
+ if (row < 0 || col < 0 || row >= base_screen_rows
+ || col >= base_screen_cols)
+ return FALSE;
+
+ int off = row * base_screen_cols + col;
+ if (linep != NULL)
+ *linep = base_screenlines[off];
+ if (attrp != NULL)
+ *attrp = base_screenattrs[off];
+ if (ucp != NULL)
+ {
+ if (enc_utf8 && base_screenlinesuc != NULL)
+ *ucp = base_screenlinesuc[off];
+ else
+ *ucp = 0;
+ }
+ return TRUE;
+}
+
+/*
+ * Set the base screen cell saved before drawing opacity popups.
+ * Used to update the snapshot after blending a layer.
+ */
+ void
+popup_set_base_screen_cell(int row, int col, schar_T line, int attr, u8char_T uc)
+{
+ if (base_screenlines == NULL || base_screenattrs == NULL)
+ return;
+ if (row < 0 || col < 0 || row >= base_screen_rows
+ || col >= base_screen_cols)
+ return;
+
+ int off = row * base_screen_cols + col;
+ base_screenlines[off] = line;
+ base_screenattrs[off] = attr;
+ if (enc_utf8 && base_screenlinesuc != NULL)
+ base_screenlinesuc[off] = uc;
+}
+#endif
+
/*
* Draw a single padding cell with opacity blending.
* Restores background from saved data and blends with popup attribute.
*/
- static void
+static void
draw_opacity_padding_cell(
int row,
int col,
int save_start_row,
int save_start_col,
int save_rows,
- int save_cols)
+ int save_cols,
+ int pad_start_col,
+ int pad_end_col)
{
int off = LineOffset[row] + col;
int r = row - save_start_row;
if (r >= 0 && r < save_rows && c >= 0 && c < save_cols)
{
int save_off = r * save_cols + c;
+ // If this is the second cell of a wide background character, blend
+ // the wide character instead of overwriting it.
+ if (enc_utf8 && saved_screenlinesuc != NULL)
+ {
+ int base_col = col - 1;
+ int base_off = off - 1;
+ int base_save_off = save_off - 1;
+ int wide_prev = FALSE;
+
+ // Prefer current screen state for detecting a wide char, since the
+ // saved data may not contain a reliable right-half marker.
+ if (base_off >= 0 && ScreenLines != NULL)
+ {
+ if (ScreenLinesUC != NULL
+ && ScreenLinesUC[base_off] != 0
+ && utf_char2cells(ScreenLinesUC[base_off]) == 2
+ && ScreenLines[off] == 0)
+ wide_prev = TRUE;
+ }
+ if (!wide_prev && save_off > 0)
+ {
+ if (saved_screenlinesuc[save_off - 1] != 0
+ && utf_char2cells(saved_screenlinesuc[save_off - 1]) == 2
+ && saved_screenlines[save_off] == 0)
+ wide_prev = TRUE;
+ }
+
+ if (wide_prev && base_col >= 0)
+ {
+ // If the wide character starts outside the padding area, do not
+ // overwrite it. Use the base screen cell if available.
+ if (base_col < pad_start_col)
+ {
+ if (ScreenLinesUC != NULL
+ && ScreenLinesUC[base_off] != 0
+ && utf_char2cells(ScreenLinesUC[base_off]) == 2)
+ {
+ // The left half still has the wide char on screen.
+ // Clear it to a space.
+ ScreenLines[base_off] = ' ';
+ ScreenLinesUC[base_off] = 0;
+ ScreenAttrs[base_off] = saved_screenattrs[base_save_off];
+ screen_char(base_off, row, base_col);
+
+ // Draw padding in the right half.
+ ScreenLines[off] = ' ';
+ ScreenAttrs[off] = saved_screenattrs[save_off];
+ if (enc_utf8)
+ ScreenLinesUC[off] = 0;
+ int popup_attr_val =
+ get_wcr_attr(screen_opacity_popup);
+ int blend = screen_opacity_popup->w_popup_blend;
+ ScreenAttrs[off] = hl_blend_attr(ScreenAttrs[off],
+ popup_attr_val, blend, TRUE);
+ popup_set_base_screen_cell(row, col,
+ ScreenLines[off], ScreenAttrs[off],
+ ScreenLinesUC[off]);
+ screen_char(off, row, col);
+ return;
+ }
+
+ // screen_line() already cleared the base cell (popup
+ // content was a space). Restore the full wide char from
+ // saved background so it shows through with opacity.
+ if (base_save_off >= 0
+ && saved_screenlinesuc != NULL
+ && saved_screenlinesuc[base_save_off] != 0
+ && utf_char2cells(
+ saved_screenlinesuc[base_save_off]) == 2)
+ {
+ ScreenLines[base_off] =
+ saved_screenlines[base_save_off];
+ ScreenLinesUC[base_off] =
+ saved_screenlinesuc[base_save_off];
+ ScreenLines[off] = saved_screenlines[save_off];
+ ScreenLinesUC[off] = saved_screenlinesuc[save_off];
+ ScreenAttrs[base_off] =
+ saved_screenattrs[base_save_off];
+ ScreenAttrs[off] = saved_screenattrs[save_off];
+
+ int popup_attr_val =
+ get_wcr_attr(screen_opacity_popup);
+ int blend = screen_opacity_popup->w_popup_blend;
+ ScreenAttrs[base_off] = hl_blend_attr(
+ ScreenAttrs[base_off],
+ popup_attr_val, blend, TRUE);
+ ScreenAttrs[off] = ScreenAttrs[base_off];
+ popup_set_base_screen_cell(row, base_col,
+ ScreenLines[base_off],
+ ScreenAttrs[base_off],
+ ScreenLinesUC[base_off]);
+ popup_set_base_screen_cell(row, col,
+ ScreenLines[off], ScreenAttrs[off],
+ ScreenLinesUC[off]);
+ screen_char(base_off, row, base_col);
+ return;
+ }
+
+ // Draw padding in the right half.
+ ScreenLines[off] = ' ';
+ ScreenAttrs[off] = saved_screenattrs[save_off];
+ if (enc_utf8 && ScreenLinesUC != NULL)
+ ScreenLinesUC[off] = 0;
+ int popup_attr_val = get_wcr_attr(screen_opacity_popup);
+ int blend = screen_opacity_popup->w_popup_blend;
+ ScreenAttrs[off] = hl_blend_attr(ScreenAttrs[off],
+ popup_attr_val, blend, TRUE);
+ popup_set_base_screen_cell(row, col, ScreenLines[off],
+ ScreenAttrs[off], ScreenLinesUC[off]);
+ screen_char(off, row, col);
+ return;
+ }
+
+ // Base cell is inside the saved area, redraw the wide char.
+ if (save_off > 0)
+ {
+ ScreenLines[base_off] = saved_screenlines[base_save_off];
+ ScreenAttrs[base_off] = saved_screenattrs[base_save_off];
+ ScreenLines[off] = saved_screenlines[save_off];
+ ScreenAttrs[off] = saved_screenattrs[save_off];
+ ScreenLinesUC[base_off] = saved_screenlinesuc[base_save_off];
+ ScreenLinesUC[off] = saved_screenlinesuc[save_off];
+
+ int popup_attr_val = get_wcr_attr(screen_opacity_popup);
+ int blend = screen_opacity_popup->w_popup_blend;
+ ScreenAttrs[base_off] = hl_blend_attr(ScreenAttrs[base_off],
+ popup_attr_val, blend, TRUE);
+ ScreenAttrs[off] = ScreenAttrs[base_off];
+ popup_set_base_screen_cell(row, base_col, ScreenLines[base_off],
+ ScreenAttrs[base_off], ScreenLinesUC[base_off]);
+ popup_set_base_screen_cell(row, col, ScreenLines[off],
+ ScreenAttrs[off], ScreenLinesUC[off]);
+ screen_char(base_off, row, base_col);
+ }
+ return;
+ }
+ }
ScreenLines[off] = saved_screenlines[save_off];
ScreenAttrs[off] = saved_screenattrs[save_off];
if (enc_utf8 && saved_screenlinesuc != NULL)
ScreenLinesUC[off] = saved_screenlinesuc[save_off];
+
+ // If the saved character is wide and would extend past the padding
+ // area into the content area, replace with a space to avoid
+ // corrupting the content.
+ if (enc_utf8 && ScreenLinesUC[off] != 0
+ && utf_char2cells(ScreenLinesUC[off]) == 2
+ && col + 1 >= pad_end_col)
+ {
+ ScreenLines[off] = ' ';
+ ScreenLinesUC[off] = 0;
+ }
+
int popup_attr_val = get_wcr_attr(screen_opacity_popup);
int blend = screen_opacity_popup->w_popup_blend;
ScreenAttrs[off] = hl_blend_attr(ScreenAttrs[off],
popup_attr_val, blend, TRUE);
+ popup_set_base_screen_cell(row, col, ScreenLines[off],
+ ScreenAttrs[off], ScreenLinesUC[off]);
screen_char(off, row, col);
}
}
/*
* Fill a rectangular padding area with opacity blending.
*/
- static void
+static void
fill_opacity_padding(
int start_row,
int end_row,
for (int pad_col = start_col; pad_col < end_col; pad_col++)
draw_opacity_padding_cell(pad_row, pad_col,
saved_screenlines, saved_screenattrs, saved_screenlinesuc,
- save_start_row, save_start_col, save_rows, save_cols);
+ save_start_row, save_start_col, save_rows, save_cols,
+ start_col, end_col);
}
/*
// so that the window with a higher zindex is drawn later, thus goes on
// top.
popup_reset_handled(POPUP_HANDLED_5);
+#ifdef FEAT_PROP_POPUP
+ if (base_screenlines != NULL)
+ {
+ vim_free(base_screenlines);
+ base_screenlines = NULL;
+ }
+ if (base_screenattrs != NULL)
+ {
+ vim_free(base_screenattrs);
+ base_screenattrs = NULL;
+ }
+ if (base_screenlinesuc != NULL)
+ {
+ vim_free(base_screenlinesuc);
+ base_screenlinesuc = NULL;
+ }
+ base_screen_rows = 0;
+ base_screen_cols = 0;
+#endif
while ((wp = find_next_popup(TRUE, POPUP_HANDLED_5)) != NULL)
{
int title_len = 0;
else
screen_opacity_popup = NULL;
+#ifdef FEAT_PROP_POPUP
+ if (screen_opacity_popup != NULL)
+ {
+ if (base_screenlines != NULL)
+ {
+ vim_free(base_screenlines);
+ base_screenlines = NULL;
+ }
+ if (base_screenattrs != NULL)
+ {
+ vim_free(base_screenattrs);
+ base_screenattrs = NULL;
+ }
+ if (base_screenlinesuc != NULL)
+ {
+ vim_free(base_screenlinesuc);
+ base_screenlinesuc = NULL;
+ }
+
+ base_screen_rows = screen_Rows;
+ base_screen_cols = screen_Columns;
+ base_screenlines = ALLOC_MULT(schar_T,
+ base_screen_rows * base_screen_cols);
+ base_screenattrs = ALLOC_MULT(int,
+ base_screen_rows * base_screen_cols);
+ if (enc_utf8)
+ base_screenlinesuc = ALLOC_MULT(u8char_T,
+ base_screen_rows * base_screen_cols);
+
+ if (base_screenlines != NULL && base_screenattrs != NULL)
+ {
+ for (int r = 0; r < base_screen_rows; r++)
+ {
+ int off = LineOffset[r];
+ int base_off = r * base_screen_cols;
+ for (int c = 0; c < base_screen_cols; c++)
+ {
+ base_screenlines[base_off + c] = ScreenLines[off + c];
+ base_screenattrs[base_off + c] = ScreenAttrs[off + c];
+ if (enc_utf8 && base_screenlinesuc != NULL)
+ base_screenlinesuc[base_off + c] =
+ ScreenLinesUC[off + c];
+ }
+ }
+ }
+ }
+#endif
+
// Save background ScreenLines for padding opacity.
// We need to save it before win_update() overwrites it.
schar_T *saved_screenlines = NULL;
save_rows = wp->w_popup_padding[0] + wp->w_height + wp->w_popup_padding[2];
save_cols = wp->w_popup_padding[3] + wp->w_width + wp->w_popup_padding[1];
+ // Include one column to the left to handle wide chars that overlap
+ // the padding boundary.
+ if (save_start_col > 0)
+ {
+ --save_start_col;
+ ++save_cols;
+ }
+
// Allocate buffers
saved_screenlines = ALLOC_MULT(schar_T, save_rows * save_cols);
saved_screenattrs = ALLOC_MULT(int, save_rows * save_cols);
col = 0;
}
if (pad_left > 0)
- screen_fill(row, row + 1, col, col + pad_left,
+ {
+ if (screen_opacity_popup != NULL && saved_screenlines != NULL)
+ fill_opacity_padding(row, row + 1, col, col + pad_left,
+ saved_screenlines, saved_screenattrs,
+ saved_screenlinesuc, save_start_row,
+ save_start_col, save_rows, save_cols);
+ else
+ screen_fill(row, row + 1, col, col + pad_left,
' ', ' ', popup_attr);
+ }
}
// scrollbar
if (wp->w_has_scrollbar)
+ wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol;
int pad_col_end = pad_col_start + wp->w_popup_padding[1];
- screen_fill(row, row + 1, pad_col_start, pad_col_end,
+ if (screen_opacity_popup != NULL && saved_screenlines != NULL)
+ fill_opacity_padding(row, row + 1, pad_col_start, pad_col_end,
+ saved_screenlines, saved_screenattrs,
+ saved_screenlinesuc, save_start_row, save_start_col,
+ save_rows, save_cols);
+ else
+ screen_fill(row, row + 1, pad_col_start, pad_col_end,
' ', ' ', popup_attr);
}
}
#endif
}
+#ifdef FEAT_PROP_POPUP
+ if (base_screenlines != NULL)
+ {
+ vim_free(base_screenlines);
+ base_screenlines = NULL;
+ }
+ if (base_screenattrs != NULL)
+ {
+ vim_free(base_screenattrs);
+ base_screenattrs = NULL;
+ }
+ if (base_screenlinesuc != NULL)
+ {
+ vim_free(base_screenlinesuc);
+ base_screenlinesuc = NULL;
+ }
+ base_screen_rows = 0;
+ base_screen_cols = 0;
+#endif
+
#if defined(FEAT_SEARCH_EXTRA)
// In case win_update() called start_search_hl().
end_search_hl();