mirror of
https://github.com/vim/vim.git
synced 2026-05-28 00:21:37 +02:00
patch 9.2.0469: popup: textprop-anchored popups bleed past host window edges
Problem: A popup anchored to a text property in a split window is
positioned relative to the screen and may extend into
adjacent splits or off-screen regions. There is no way to
confine the popup to the window that contains the textprop.
Solution: Add the "clipwindow" popup option to allow clipping the text
property popup to the host window (Yasuhiro Matsumoto).
Adds a "clipwindow" boolean option to popup_create()/popup_setoptions().
When set on a textprop-anchored popup, the popup's drawn extent is
confined to its host (textprop) window's content rectangle so the popup
no longer bleeds across a horizontal split's statusline (top/bottom) or
a vsplit's separator (right) into another window.
The popup keeps its full logical size and position; only the rows or
columns that fall outside the host window's content area are skipped
during drawing, so a popup that scrolls toward the host's edge looks
visually "cut off" without its borders being relocated. popup_getoptions
and popup_getpos continue to report the unclipped geometry.
Implementation:
- w_popup_topoff / w_popup_bottomoff record how many rows of the
popup fall outside the host on each side. popup_adjust_position()
computes them from the host rectangle after the logical layout is
finalised, and update_popups() and the popup-mask builder subtract
them when emitting cells/borders/scrollbar and when marking
popup-owned cells. win_update() is bracketed by transient
w_height/w_topline/w_winrow adjustments so the buffer's drawn
content matches the visible row range.
- w_popup_rightclip is the horizontal counterpart for the host's
right edge: the right border, padding and content columns past
the host are not drawn. win_update() is bracketed by a transient
w_width reduction so the buffer text is not written past the
host's right edge either.
- When the textprop scrolls just above the host window's top, the
popup is kept visible by extending the prop search above topline
(new helper find_prop_in_lines) and synthesising a negative
screen_row so the top-clip path can roll the popup off the top.
When the textprop has scrolled far enough that even the bottom
border would overlap the host edge -- or when the popup would
overflow the host's left edge at all -- the popup is hidden, and
unhidden again once it comes back within range.
- The "reduce-height" / "clamp winrow to 0" fallbacks in
popup_adjust_position are bypassed for host-clipped popups so the
popup keeps its natural anchored position instead of being
snapped to the screen edge.
Left-edge partial clipping is intentionally not supported: it
would require shrinking the buffer width during win_update, which
reflows wrapped lines and corrupts the displayed content; the
popup is hidden instead.
closes: #20166
Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
ef1ecc3b61
commit
e3d9929109
+35
-1
@@ -1,4 +1,4 @@
|
||||
*popup.txt* For Vim version 9.2. Last change: 2026 May 01
|
||||
*popup.txt* For Vim version 9.2. Last change: 2026 May 10
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -712,6 +712,15 @@ The second argument of |popup_create()| is a dictionary with options:
|
||||
when "textprop" is present.
|
||||
textpropid Used to identify the text property when "textprop" is
|
||||
present. Use zero to reset.
|
||||
clipwindow Only used when "textprop" is set. When TRUE the popup
|
||||
is kept within the window containing the text
|
||||
property: if the text property scrolls past that
|
||||
window's top, bottom, left or right edge, the popup
|
||||
is clipped at that edge instead of being drawn
|
||||
outside it. Once the text property has scrolled out
|
||||
of the window the popup is hidden.
|
||||
Default FALSE.
|
||||
See |popup-clipwindow|.
|
||||
fixed When FALSE (the default), and:
|
||||
- "pos" is "botleft" or "topleft", and
|
||||
- the popup would be truncated at the right edge of
|
||||
@@ -949,6 +958,31 @@ If the window for which the popup was defined is closed, the popup is closed.
|
||||
If the popup cannot fit in the desired position, it may show at a nearby
|
||||
position.
|
||||
|
||||
|
||||
CLIP TEXTPROP POPUP TO HOST WINDOW *popup-clipwindow*
|
||||
|
||||
When the popup is anchored to a text property in a split window, the popup is
|
||||
by default drawn relative to the whole screen and may extend past the edges of
|
||||
the window that contains the text property (the "host window"). Setting
|
||||
"clipwindow" to TRUE keeps the popup within window's content area:
|
||||
parts of the popup that fall outside the window are clipped, and the popup is
|
||||
hidden once the text property has scrolled entirely past one of the edges.
|
||||
|
||||
Example: a tall popup anchored above the cursor that should never spill into
|
||||
the window below the split: >
|
||||
call popup_create(body, #{
|
||||
\ textprop: 'marker',
|
||||
\ textpropid: id,
|
||||
\ pos: 'topleft',
|
||||
\ line: -1, col: 0,
|
||||
\ posinvert: v:false,
|
||||
\ clipwindow: v:true,
|
||||
\ })
|
||||
<
|
||||
With "posinvert" left at its default (TRUE) the popup may be flipped to the
|
||||
opposite side of the text property when there is no room; set it to FALSE to
|
||||
keep the requested side and rely on "clipwindow" to clip the overflow.
|
||||
|
||||
Some hints:
|
||||
- To avoid collision with other plugins the text property type name has to be
|
||||
unique. You can also use the "bufnr" item to make it local to a buffer.
|
||||
|
||||
@@ -9791,6 +9791,7 @@ popt-option print.txt /*popt-option*
|
||||
popup popup.txt /*popup*
|
||||
popup-buffer popup.txt /*popup-buffer*
|
||||
popup-callback popup.txt /*popup-callback*
|
||||
popup-clipwindow popup.txt /*popup-clipwindow*
|
||||
popup-close popup.txt /*popup-close*
|
||||
popup-examples popup.txt /*popup-examples*
|
||||
popup-filter popup.txt /*popup-filter*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
*version9.txt* For Vim version 9.2. Last change: 2026 May 05
|
||||
*version9.txt* For Vim version 9.2. Last change: 2026 May 10
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -52588,6 +52588,7 @@ Popups ~
|
||||
- 'previewpopup' supports the same values as 'completepopup' (except for
|
||||
"align").
|
||||
- Support "opacity" setting for 'completepopup' option.
|
||||
- Support for clipping textproperty popups |popup-clipwindow|.
|
||||
|
||||
Diff mode ~
|
||||
---------
|
||||
|
||||
+516
-61
@@ -70,6 +70,10 @@ typedef struct {
|
||||
int leftcol;
|
||||
int leftoff;
|
||||
int has_scrollbar;
|
||||
int topoff;
|
||||
int bottomoff;
|
||||
int leftclip;
|
||||
int rightclip;
|
||||
} popup_layout_T;
|
||||
|
||||
static poppos_entry_T poppos_entries[] = {
|
||||
@@ -838,6 +842,15 @@ apply_general_options(win_T *wp, dict_T *dict)
|
||||
wp->w_popup_flags &= ~POPF_POSINVERT;
|
||||
}
|
||||
|
||||
nr = dict_get_bool(dict, "clipwindow", -1);
|
||||
if (nr != -1)
|
||||
{
|
||||
if (nr)
|
||||
wp->w_popup_flags |= POPF_CLIPWINDOW;
|
||||
else
|
||||
wp->w_popup_flags &= ~POPF_CLIPWINDOW;
|
||||
}
|
||||
|
||||
nr = dict_get_bool(dict, "resize", -1);
|
||||
if (nr != -1)
|
||||
{
|
||||
@@ -1320,6 +1333,320 @@ popup_extra_width(win_T *wp)
|
||||
+ wp->w_has_scrollbar;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the host window used to clip popup "wp" when POPF_CLIPWINDOW is set,
|
||||
* or NULL when no clipping should be applied (option off, or the host window
|
||||
* is no longer valid). The textprop window is used as the host; popups not
|
||||
* anchored to a textprop are not clipped.
|
||||
*/
|
||||
static win_T *
|
||||
popup_get_clipwin(win_T *wp)
|
||||
{
|
||||
if (!(wp->w_popup_flags & POPF_CLIPWINDOW))
|
||||
return NULL;
|
||||
if (win_valid(wp->w_popup_prop_win))
|
||||
return wp->w_popup_prop_win;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Per-popup clip geometry derived from w_popup_{top,bottom}off and
|
||||
// w_popup_{left,right}clip. Filled by popup_compute_clip().
|
||||
//
|
||||
// *_extra : original border+padding at each edge.
|
||||
// clip_*_content : how many *content* rows/cols are clipped at each edge
|
||||
// (border/padding is consumed first; the rest comes off
|
||||
// w_height/w_width). >= 0.
|
||||
// eff_*_extra : 0 when that edge is clipped (border+padding gone),
|
||||
// otherwise the original *_extra.
|
||||
// eff_border[],
|
||||
// eff_padding[] : per-edge border/padding sizes (indexed [top,right,bot,left]
|
||||
// matching wp->w_popup_border / wp->w_popup_padding). At a
|
||||
// clipped edge they collapse to 0; elsewhere they keep the
|
||||
// original size. Drawing code can replace
|
||||
// `wp->w_popup_border[N] > 0 && wp->w_popup_*clip == 0`
|
||||
// with a single `cl.eff_border[N] > 0` test.
|
||||
// eff_height : drawn extent = eff_top_extra + visible content + eff_bot_extra.
|
||||
// eff_width : drawn extent = eff_left_extra + visible content + eff_right_extra
|
||||
// (does NOT include w_leftcol or scrollbar; see callers).
|
||||
typedef struct {
|
||||
int top_extra;
|
||||
int bot_extra;
|
||||
int left_extra;
|
||||
int right_extra;
|
||||
|
||||
int clip_top_content;
|
||||
int clip_bot_content;
|
||||
int clip_left_content;
|
||||
int clip_right_content;
|
||||
|
||||
int eff_top_extra;
|
||||
int eff_bot_extra;
|
||||
int eff_left_extra;
|
||||
int eff_right_extra;
|
||||
|
||||
int eff_border[4];
|
||||
int eff_padding[4];
|
||||
|
||||
int eff_height;
|
||||
int eff_width;
|
||||
} popup_clip_T;
|
||||
|
||||
static void
|
||||
popup_compute_clip(win_T *wp, popup_clip_T *cl)
|
||||
{
|
||||
int h, w;
|
||||
|
||||
cl->top_extra = popup_top_extra(wp);
|
||||
cl->bot_extra = wp->w_popup_padding[2] + wp->w_popup_border[2];
|
||||
cl->left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3];
|
||||
cl->right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1];
|
||||
|
||||
cl->clip_top_content = wp->w_popup_topoff - cl->top_extra;
|
||||
if (cl->clip_top_content < 0)
|
||||
cl->clip_top_content = 0;
|
||||
cl->clip_bot_content = wp->w_popup_bottomoff - cl->bot_extra;
|
||||
if (cl->clip_bot_content < 0)
|
||||
cl->clip_bot_content = 0;
|
||||
cl->clip_left_content = wp->w_popup_leftclip - cl->left_extra;
|
||||
if (cl->clip_left_content < 0)
|
||||
cl->clip_left_content = 0;
|
||||
cl->clip_right_content = wp->w_popup_rightclip - cl->right_extra;
|
||||
if (cl->clip_right_content < 0)
|
||||
cl->clip_right_content = 0;
|
||||
|
||||
cl->eff_top_extra = wp->w_popup_topoff > 0 ? 0 : cl->top_extra;
|
||||
cl->eff_bot_extra = wp->w_popup_bottomoff > 0 ? 0 : cl->bot_extra;
|
||||
cl->eff_left_extra = wp->w_popup_leftclip > 0 ? 0 : cl->left_extra;
|
||||
cl->eff_right_extra = wp->w_popup_rightclip > 0 ? 0 : cl->right_extra;
|
||||
|
||||
cl->eff_border[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_border[0];
|
||||
cl->eff_border[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_border[1];
|
||||
cl->eff_border[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_border[2];
|
||||
cl->eff_border[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_border[3];
|
||||
|
||||
cl->eff_padding[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_padding[0];
|
||||
cl->eff_padding[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_padding[1];
|
||||
cl->eff_padding[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_padding[2];
|
||||
cl->eff_padding[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_padding[3];
|
||||
|
||||
h = wp->w_height - cl->clip_top_content - cl->clip_bot_content;
|
||||
if (h < 0)
|
||||
h = 0;
|
||||
cl->eff_height = cl->eff_top_extra + h + cl->eff_bot_extra;
|
||||
|
||||
w = wp->w_width - cl->clip_left_content - cl->clip_right_content;
|
||||
if (w < 0)
|
||||
w = 0;
|
||||
cl->eff_width = cl->eff_left_extra + w + cl->eff_right_extra;
|
||||
}
|
||||
|
||||
// Snapshot of the popup window geometry that update_popups() temporarily
|
||||
// mutates so that win_update() draws within the host-window clip rectangle.
|
||||
// Saved before the clip is applied, restored after win_update() returns so
|
||||
// callers continue to see the popup's logical geometry.
|
||||
// Field names omit the "w_" prefix to avoid clashing with struct-field
|
||||
// macros like w_p_wrap (= w_onebuf_opt.wo_wrap).
|
||||
typedef struct {
|
||||
int height;
|
||||
int width;
|
||||
int winrow;
|
||||
int wincol;
|
||||
int leftcol;
|
||||
int p_wrap;
|
||||
linenr_T topline;
|
||||
} popup_geom_save_T;
|
||||
|
||||
static void
|
||||
popup_geom_save(win_T *wp, popup_geom_save_T *sv)
|
||||
{
|
||||
sv->height = wp->w_height;
|
||||
sv->width = wp->w_width;
|
||||
sv->winrow = wp->w_winrow;
|
||||
sv->wincol = wp->w_wincol;
|
||||
sv->leftcol = wp->w_leftcol;
|
||||
sv->p_wrap = wp->w_p_wrap;
|
||||
sv->topline = wp->w_topline;
|
||||
}
|
||||
|
||||
static void
|
||||
popup_geom_restore(win_T *wp, popup_geom_save_T *sv)
|
||||
{
|
||||
wp->w_p_wrap = sv->p_wrap;
|
||||
wp->w_leftcol = sv->leftcol;
|
||||
wp->w_wincol = sv->wincol;
|
||||
wp->w_winrow = sv->winrow;
|
||||
wp->w_topline = sv->topline;
|
||||
wp->w_width = sv->width;
|
||||
wp->w_height = sv->height;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute a screen row for a textprop that has scrolled above the host
|
||||
* window's top. textpos2screenpos() cannot return a row above topline, so we
|
||||
* probe at topline to fill the screen_{scol,ccol,ecol} column mapping, then
|
||||
* extrapolate a (possibly-negative) row by counting how many buffer lines lie
|
||||
* between the prop and topline. The popup_topoff clip path then turns the
|
||||
* negative row into a top-clip animation as the prop rolls off the top edge.
|
||||
*/
|
||||
static void
|
||||
popup_screenpos_above_top(
|
||||
win_T *prop_win,
|
||||
pos_T *pos,
|
||||
linenr_T prop_lnum,
|
||||
int *screen_row,
|
||||
int *screen_scol,
|
||||
int *screen_ccol,
|
||||
int *screen_ecol)
|
||||
{
|
||||
pos_T probe = *pos;
|
||||
|
||||
probe.lnum = prop_win->w_topline;
|
||||
textpos2screenpos(prop_win, &probe,
|
||||
screen_row, screen_scol, screen_ccol, screen_ecol);
|
||||
*screen_row = prop_win->w_winrow + 1
|
||||
- (int)(prop_win->w_topline - prop_lnum);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide popup "wp" because its anchoring textprop is no longer reachable.
|
||||
* Marks the popup as POPF_HIDDEN (no-op when already hidden) and schedules a
|
||||
* redraw of the host window so any leftover decorations are cleared.
|
||||
*/
|
||||
static void
|
||||
popup_hide_for_textprop(win_T *wp)
|
||||
{
|
||||
if ((wp->w_popup_flags & POPF_HIDDEN) != 0)
|
||||
return;
|
||||
wp->w_popup_flags |= POPF_HIDDEN;
|
||||
if (win_valid(wp->w_popup_prop_win))
|
||||
redraw_win_later(wp->w_popup_prop_win, UPD_SOME_VALID);
|
||||
}
|
||||
|
||||
/*
|
||||
* For "clipwindow" popups: search the lines above prop_win->w_topline for the
|
||||
* popup's anchoring textprop and report whether one was found. When
|
||||
* "max_reach" is > 0, only the last "max_reach" lines before topline are
|
||||
* scanned; pass 0 to scan all lines from line 1. Returns false when the
|
||||
* popup is not "clipwindow", topline is already at line 1, or no prop matches.
|
||||
*/
|
||||
static bool
|
||||
popup_find_prop_above_top(
|
||||
win_T *wp,
|
||||
win_T *prop_win,
|
||||
int max_reach,
|
||||
textprop_T *prop,
|
||||
linenr_T *found_lnum)
|
||||
{
|
||||
linenr_T first;
|
||||
|
||||
if (!(wp->w_popup_flags & POPF_CLIPWINDOW) || prop_win->w_topline <= 1)
|
||||
return false;
|
||||
|
||||
first = max_reach > 0 ? prop_win->w_topline - max_reach : 1;
|
||||
if (first < 1)
|
||||
first = 1;
|
||||
return find_prop_in_lines(prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
prop, found_lnum, first, prop_win->w_topline - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute and assign w_popup_topoff/bottomoff/leftclip/rightclip from the
|
||||
* host (textprop) window's content rectangle when POPF_CLIPWINDOW is set.
|
||||
* The popup's logical geometry (w_winrow, w_height, w_width) is preserved;
|
||||
* only the *off/clip fields record how much of each edge falls outside.
|
||||
* Returns true when the popup has scrolled completely past one of the host
|
||||
* edges, in which case the caller must hide it.
|
||||
*/
|
||||
static bool
|
||||
popup_compute_clipwindow_offsets(win_T *wp)
|
||||
{
|
||||
win_T *cw = popup_get_clipwin(wp);
|
||||
int extra_h, extra_w;
|
||||
int popup_top, popup_bottom, popup_left, popup_right;
|
||||
int total_h, total_w;
|
||||
|
||||
if (cw == NULL)
|
||||
return false;
|
||||
|
||||
extra_h = popup_top_extra(wp)
|
||||
+ wp->w_popup_padding[2] + wp->w_popup_border[2];
|
||||
extra_w = popup_extra_width(wp);
|
||||
|
||||
popup_top = wp->w_winrow;
|
||||
popup_bottom = wp->w_winrow + wp->w_height + extra_h;
|
||||
popup_left = wp->w_wincol;
|
||||
popup_right = wp->w_wincol + wp->w_width + extra_w;
|
||||
total_h = wp->w_height + extra_h;
|
||||
total_w = wp->w_width + extra_w;
|
||||
|
||||
if (popup_top < cw->w_winrow)
|
||||
wp->w_popup_topoff = cw->w_winrow - popup_top;
|
||||
if (popup_bottom > cw->w_winrow + cw->w_height)
|
||||
wp->w_popup_bottomoff = popup_bottom - (cw->w_winrow + cw->w_height);
|
||||
if (popup_left < cw->w_wincol)
|
||||
wp->w_popup_leftclip = cw->w_wincol - popup_left;
|
||||
if (popup_right > cw->w_wincol + cw->w_width)
|
||||
wp->w_popup_rightclip = popup_right - (cw->w_wincol + cw->w_width);
|
||||
|
||||
return wp->w_popup_topoff >= total_h
|
||||
|| wp->w_popup_bottomoff >= total_h
|
||||
|| wp->w_popup_leftclip >= total_w
|
||||
|| wp->w_popup_rightclip >= total_w;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mutate "wp"'s window geometry so win_update() draws only the rows/columns
|
||||
* that fit within the host-window clip rectangle for "clipwindow" popups.
|
||||
* The caller must save the original geometry with popup_geom_save() before
|
||||
* this call and restore it with popup_geom_restore() after win_update().
|
||||
*
|
||||
* Vertical clip: shrink w_height by the clipped content rows; advance
|
||||
* w_topline and w_winrow when rows are cut off the top so the first visible
|
||||
* content row lands on the host's top edge.
|
||||
*
|
||||
* Horizontal clip: when the right side is clipped, just shrink w_width.
|
||||
* When the left side is clipped, advance w_leftcol so the hidden buffer
|
||||
* columns scroll off and shift w_wincol so the first visible column lands on
|
||||
* the host's left edge. Disable wrap so the transient w_width reduction does
|
||||
* not reflow wrapped lines: the popup's logical width is unchanged, we just
|
||||
* want to truncate cells that fall outside the host at draw time.
|
||||
*/
|
||||
static void
|
||||
popup_apply_winupdate_clip(win_T *wp, popup_clip_T *cl)
|
||||
{
|
||||
if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0)
|
||||
{
|
||||
wp->w_height -= cl->clip_top_content + cl->clip_bot_content;
|
||||
if (wp->w_height < 0)
|
||||
wp->w_height = 0;
|
||||
if (cl->clip_top_content > 0)
|
||||
{
|
||||
wp->w_topline += cl->clip_top_content;
|
||||
wp->w_winrow += cl->clip_top_content;
|
||||
}
|
||||
}
|
||||
if (wp->w_popup_leftclip > 0 || wp->w_popup_rightclip > 0)
|
||||
{
|
||||
if (cl->clip_left_content > 0 || cl->clip_right_content > 0)
|
||||
wp->w_p_wrap = FALSE;
|
||||
if (cl->clip_right_content > 0)
|
||||
{
|
||||
wp->w_width -= cl->clip_right_content;
|
||||
if (wp->w_width < 0)
|
||||
wp->w_width = 0;
|
||||
}
|
||||
if (cl->clip_left_content > 0)
|
||||
{
|
||||
wp->w_leftcol += cl->clip_left_content;
|
||||
wp->w_wincol += cl->clip_left_content;
|
||||
wp->w_width -= cl->clip_left_content;
|
||||
if (wp->w_width < 0)
|
||||
wp->w_width = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the position and size of the popup to fit on the screen.
|
||||
*/
|
||||
@@ -1361,6 +1688,10 @@ popup_adjust_position(win_T *wp)
|
||||
wp->w_leftcol = 0;
|
||||
wp->w_popup_leftoff = 0;
|
||||
wp->w_popup_rightoff = 0;
|
||||
wp->w_popup_topoff = 0;
|
||||
wp->w_popup_bottomoff = 0;
|
||||
wp->w_popup_leftclip = 0;
|
||||
wp->w_popup_rightclip = 0;
|
||||
|
||||
// May need to update the "cursorline" highlighting, which may also change
|
||||
// "topline"
|
||||
@@ -1378,20 +1709,24 @@ popup_adjust_position(win_T *wp)
|
||||
int screen_ccol;
|
||||
int screen_ecol;
|
||||
|
||||
// Popup window is positioned relative to a text property.
|
||||
// Popup window is positioned relative to a text property. With
|
||||
// "clipwindow", keep the popup visible while the textprop has just
|
||||
// scrolled above the host's top: extrapolate a negative screen_row
|
||||
// from a prop above topline so the top-clip path can roll the popup
|
||||
// off the top edge. Unhiding is done in check_popup_unhidden().
|
||||
bool prop_above_top = false;
|
||||
if (!find_visible_prop(prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
&prop, &prop_lnum))
|
||||
{
|
||||
// Text property is no longer visible, hide the popup.
|
||||
// Unhiding the popup is done in check_popup_unhidden().
|
||||
if ((wp->w_popup_flags & POPF_HIDDEN) == 0)
|
||||
if (popup_find_prop_above_top(wp, prop_win, 0,
|
||||
&prop, &prop_lnum))
|
||||
prop_above_top = true;
|
||||
else
|
||||
{
|
||||
wp->w_popup_flags |= POPF_HIDDEN;
|
||||
if (win_valid(wp->w_popup_prop_win))
|
||||
redraw_win_later(wp->w_popup_prop_win, UPD_SOME_VALID);
|
||||
popup_hide_for_textprop(wp);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute the desired position from the position of the text
|
||||
@@ -1401,7 +1736,11 @@ popup_adjust_position(win_T *wp)
|
||||
if (wp->w_popup_pos == POPPOS_TOPLEFT
|
||||
|| wp->w_popup_pos == POPPOS_BOTLEFT)
|
||||
pos.col += prop.tp_len - 1;
|
||||
textpos2screenpos(prop_win, &pos, &screen_row,
|
||||
if (prop_above_top)
|
||||
popup_screenpos_above_top(prop_win, &pos, prop_lnum, &screen_row,
|
||||
&screen_scol, &screen_ccol, &screen_ecol);
|
||||
else
|
||||
textpos2screenpos(prop_win, &pos, &screen_row,
|
||||
&screen_scol, &screen_ccol, &screen_ecol);
|
||||
|
||||
if (screen_scol == 0)
|
||||
@@ -1771,8 +2110,11 @@ popup_adjust_position(win_T *wp)
|
||||
else if (wp->w_popup_pos == POPPOS_BOTRIGHT
|
||||
|| wp->w_popup_pos == POPPOS_BOTLEFT)
|
||||
{
|
||||
if ((wp->w_height + extra_height) <= wantline)
|
||||
// bottom aligned: may move down
|
||||
if ((wp->w_height + extra_height) <= wantline
|
||||
|| (wp->w_popup_flags & POPF_CLIPWINDOW))
|
||||
// bottom aligned: may move down. With "clipwindow" the popup
|
||||
// keeps its natural position even if it overflows the screen,
|
||||
// because the clip logic handles the overflow.
|
||||
wp->w_winrow = wantline - (wp->w_height + extra_height);
|
||||
else if (wantline * 2 >= Rows || !(wp->w_popup_flags & POPF_POSINVERT))
|
||||
{
|
||||
@@ -1838,7 +2180,7 @@ popup_adjust_position(win_T *wp)
|
||||
// make sure w_winrow is valid
|
||||
if (wp->w_winrow >= Rows)
|
||||
wp->w_winrow = Rows - 1;
|
||||
else if (wp->w_winrow < 0)
|
||||
else if (wp->w_winrow < 0 && !(wp->w_popup_flags & POPF_CLIPWINDOW))
|
||||
wp->w_winrow = 0;
|
||||
|
||||
if (wp->w_wincol + wp->w_width + extra_width
|
||||
@@ -1863,25 +2205,48 @@ popup_adjust_position(win_T *wp)
|
||||
|
||||
// Same for the bottom edge: shift up so the border/padding/shadow stays
|
||||
// on screen, and clip the height if the popup is taller than the screen.
|
||||
if (wp->w_winrow + wp->w_height + extra_height > Rows)
|
||||
wp->w_winrow = Rows - wp->w_height - extra_height;
|
||||
if (wp->w_winrow < 0)
|
||||
wp->w_winrow = 0;
|
||||
if (wp->w_winrow + wp->w_height + extra_height > Rows)
|
||||
// For "clipwindow" popups the host-window clip below handles overflow, so
|
||||
// skip these screen-edge clamps -- otherwise a synthesised negative
|
||||
// w_winrow (popup partially above the host's top edge) would be snapped
|
||||
// back to 0 and defeat the top-clip animation.
|
||||
if (!(wp->w_popup_flags & POPF_CLIPWINDOW))
|
||||
{
|
||||
int avail = Rows - wp->w_winrow - extra_height;
|
||||
wp->w_height = avail > 0 ? avail : 0;
|
||||
if (wp->w_winrow + wp->w_height + extra_height > Rows)
|
||||
wp->w_winrow = Rows - wp->w_height - extra_height;
|
||||
if (wp->w_winrow < 0)
|
||||
wp->w_winrow = 0;
|
||||
if (wp->w_winrow + wp->w_height + extra_height > Rows)
|
||||
{
|
||||
int avail = Rows - wp->w_winrow - extra_height;
|
||||
wp->w_height = avail > 0 ? avail : 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (wp->w_height != org_layout.height)
|
||||
win_comp_scroll(wp);
|
||||
|
||||
// Confine the popup to its host window for "clipwindow". The popup's
|
||||
// logical geometry stays untouched; only w_popup_topoff/bottomoff/
|
||||
// leftclip/rightclip record how many rows/columns of each edge fall
|
||||
// outside the host so the drawing code can skip them. When the popup
|
||||
// has fully scrolled past one of the host edges, hide it instead of
|
||||
// leaving stray decorations behind.
|
||||
if (popup_compute_clipwindow_offsets(wp))
|
||||
{
|
||||
popup_hide_for_textprop(wp);
|
||||
return;
|
||||
}
|
||||
|
||||
wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer);
|
||||
if (win_valid(wp->w_popup_prop_win))
|
||||
{
|
||||
wp->w_popup_prop_changedtick =
|
||||
CHANGEDTICK(wp->w_popup_prop_win->w_buffer);
|
||||
wp->w_popup_prop_topline = wp->w_popup_prop_win->w_topline;
|
||||
wp->w_popup_prop_winrow = wp->w_popup_prop_win->w_winrow;
|
||||
wp->w_popup_prop_wincol = wp->w_popup_prop_win->w_wincol;
|
||||
wp->w_popup_prop_width = wp->w_popup_prop_win->w_width;
|
||||
wp->w_popup_prop_winheight = wp->w_popup_prop_win->w_height;
|
||||
}
|
||||
|
||||
// Need to update popup_mask if the position or size changed.
|
||||
@@ -3554,6 +3919,10 @@ popup_save_layout(win_T *wp, popup_layout_T *layout)
|
||||
layout->leftcol = wp->w_leftcol;
|
||||
layout->leftoff = wp->w_popup_leftoff;
|
||||
layout->has_scrollbar = wp->w_has_scrollbar;
|
||||
layout->topoff = wp->w_popup_topoff;
|
||||
layout->bottomoff = wp->w_popup_bottomoff;
|
||||
layout->leftclip = wp->w_popup_leftclip;
|
||||
layout->rightclip = wp->w_popup_rightclip;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3568,7 +3937,11 @@ popup_layout_changed(win_T *wp, popup_layout_T *layout)
|
||||
|| layout->leftoff != wp->w_popup_leftoff
|
||||
|| layout->width != wp->w_width
|
||||
|| layout->height != wp->w_height
|
||||
|| layout->has_scrollbar != wp->w_has_scrollbar;
|
||||
|| layout->has_scrollbar != wp->w_has_scrollbar
|
||||
|| layout->topoff != wp->w_popup_topoff
|
||||
|| layout->bottomoff != wp->w_popup_bottomoff
|
||||
|| layout->leftclip != wp->w_popup_leftclip
|
||||
|| layout->rightclip != wp->w_popup_rightclip;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4298,6 +4671,8 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv)
|
||||
dict_add_number(dict, "resize", (wp->w_popup_flags & POPF_RESIZE) != 0);
|
||||
dict_add_number(dict, "posinvert",
|
||||
(wp->w_popup_flags & POPF_POSINVERT) != 0);
|
||||
dict_add_number(dict, "clipwindow",
|
||||
(wp->w_popup_flags & POPF_CLIPWINDOW) != 0);
|
||||
// Return opacity (0-100) by converting from internal blend value
|
||||
dict_add_number(dict, "opacity",
|
||||
(wp->w_popup_flags & POPF_OPACITY) ? 100 - wp->w_popup_blend : 100);
|
||||
@@ -4765,11 +5140,23 @@ check_popup_unhidden(win_T *wp)
|
||||
{
|
||||
textprop_T prop;
|
||||
linenr_T lnum;
|
||||
bool found = false;
|
||||
|
||||
if ((wp->w_popup_flags & POPF_HIDDEN_FORCE) == 0
|
||||
&& find_visible_prop(wp->w_popup_prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
&prop, &lnum))
|
||||
if ((wp->w_popup_flags & POPF_HIDDEN_FORCE) != 0)
|
||||
return FALSE;
|
||||
if (find_visible_prop(wp->w_popup_prop_win,
|
||||
wp->w_popup_prop_type, wp->w_popup_prop_id,
|
||||
&prop, &lnum))
|
||||
found = true;
|
||||
// The textprop may have scrolled just above the host window's top.
|
||||
// Unhide the popup so popup_adjust_position() can roll it partially
|
||||
// onto the host's top edge via the top-clip path. Limit the search
|
||||
// to the popup's own height so we do not resurrect a popup whose
|
||||
// prop is already further off-screen than the popup can extend.
|
||||
else if (popup_find_prop_above_top(wp, wp->w_popup_prop_win,
|
||||
popup_height(wp), &prop, &lnum))
|
||||
found = true;
|
||||
if (found)
|
||||
{
|
||||
wp->w_popup_flags &= ~POPF_HIDDEN;
|
||||
wp->w_popup_prop_topline = 0; // force repositioning
|
||||
@@ -4793,7 +5180,11 @@ popup_need_position_adjust(win_T *wp)
|
||||
if (win_valid(wp->w_popup_prop_win)
|
||||
&& (wp->w_popup_prop_changedtick
|
||||
!= CHANGEDTICK(wp->w_popup_prop_win->w_buffer)
|
||||
|| wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline))
|
||||
|| wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline
|
||||
|| wp->w_popup_prop_winrow != wp->w_popup_prop_win->w_winrow
|
||||
|| wp->w_popup_prop_wincol != wp->w_popup_prop_win->w_wincol
|
||||
|| wp->w_popup_prop_width != wp->w_popup_prop_win->w_width
|
||||
|| wp->w_popup_prop_winheight != wp->w_popup_prop_win->w_height))
|
||||
return TRUE;
|
||||
|
||||
// May need to adjust the width if the cursor moved.
|
||||
@@ -5086,7 +5477,18 @@ may_update_popup_mask(int type)
|
||||
}
|
||||
|
||||
width = popup_width(wp);
|
||||
height = popup_height(wp);
|
||||
// Match the drawn extent computed by update_popups so that cells
|
||||
// outside the clipped popup are not marked as popup-owned and the
|
||||
// background window can draw through them.
|
||||
if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0)
|
||||
{
|
||||
popup_clip_T cl;
|
||||
|
||||
popup_compute_clip(wp, &cl);
|
||||
height = cl.eff_height;
|
||||
}
|
||||
else
|
||||
height = popup_height(wp);
|
||||
popup_update_mask(wp, width, height);
|
||||
|
||||
// Popup with partial transparency do not block lower layers from
|
||||
@@ -5095,18 +5497,25 @@ may_update_popup_mask(int type)
|
||||
if ((wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0)
|
||||
continue;
|
||||
|
||||
for (line = wp->w_winrow;
|
||||
line < wp->w_winrow + height && line < screen_Rows; ++line)
|
||||
for (col = wp->w_wincol;
|
||||
col < wp->w_wincol + width - wp->w_popup_leftoff
|
||||
&& col < screen_Columns; ++col)
|
||||
if (wp->w_zindex < POPUPMENU_ZINDEX
|
||||
&& pum_visible()
|
||||
&& pum_under_menu(line, col, FALSE))
|
||||
mask[line * screen_Columns + col] = POPUPMENU_ZINDEX;
|
||||
else if (wp->w_popup_mask_cells == NULL
|
||||
|| !popup_masked(wp, width, height, col, line))
|
||||
mask[line * screen_Columns + col] = wp->w_zindex;
|
||||
{
|
||||
int mask_start = wp->w_winrow + wp->w_popup_topoff;
|
||||
int mask_end = mask_start + height;
|
||||
int mask_col_start = wp->w_wincol + wp->w_popup_leftclip;
|
||||
int mask_col_end = wp->w_wincol + width - wp->w_popup_leftoff
|
||||
- wp->w_popup_rightclip;
|
||||
|
||||
for (line = mask_start;
|
||||
line < mask_end && line < screen_Rows; ++line)
|
||||
for (col = mask_col_start;
|
||||
col < mask_col_end && col < screen_Columns; ++col)
|
||||
if (wp->w_zindex < POPUPMENU_ZINDEX
|
||||
&& pum_visible()
|
||||
&& pum_under_menu(line, col, FALSE))
|
||||
mask[line * screen_Columns + col] = POPUPMENU_ZINDEX;
|
||||
else if (wp->w_popup_mask_cells == NULL
|
||||
|| !popup_masked(wp, width, height, col, line))
|
||||
mask[line * screen_Columns + col] = wp->w_zindex;
|
||||
}
|
||||
}
|
||||
|
||||
// Only check which lines are to be updated if not already
|
||||
@@ -5528,6 +5937,14 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
{
|
||||
int title_len = 0;
|
||||
int title_wincol;
|
||||
popup_clip_T cl;
|
||||
|
||||
// Compute the clip geometry once per iteration; w_popup_*off/clip,
|
||||
// w_height, w_width, w_popup_border and w_popup_padding are stable
|
||||
// for the duration of this iteration (popup_apply_winupdate_clip()
|
||||
// mutates w_height/w_width temporarily but the result is restored
|
||||
// before any code below reads cl again).
|
||||
popup_compute_clip(wp, &cl);
|
||||
|
||||
override_success = push_highlight_overrides(wp->w_hl, wp->w_hl_len);
|
||||
|
||||
@@ -5613,13 +6030,25 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
// Draw the popup text, unless it's off screen.
|
||||
if (wp->w_winrow < screen_Rows && wp->w_wincol < screen_Columns)
|
||||
{
|
||||
popup_geom_save_T saved;
|
||||
|
||||
popup_geom_save(wp, &saved);
|
||||
|
||||
// May need to update the "cursorline" highlighting, which may also
|
||||
// change "topline"
|
||||
if (wp->w_popup_last_curline != wp->w_cursor.lnum)
|
||||
popup_highlight_curline(wp);
|
||||
|
||||
// Clip the buffer's drawn extent to the host window when
|
||||
// "clipwindow" is set. The transient mutations are reverted by
|
||||
// popup_geom_restore() so callers continue to see the popup's
|
||||
// logical geometry via popup_getoptions/popup_getpos.
|
||||
popup_apply_winupdate_clip(wp, &cl);
|
||||
|
||||
win_update(wp);
|
||||
|
||||
popup_geom_restore(wp, &saved);
|
||||
|
||||
// move the cursor into the visible lines, otherwise executing
|
||||
// commands with win_execute() may cause the text to jump.
|
||||
if (wp->w_cursor.lnum < wp->w_topline)
|
||||
@@ -5631,6 +6060,12 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
wp->w_winrow -= top_off;
|
||||
wp->w_wincol -= left_extra;
|
||||
|
||||
// "clipwindow" with top-clip shifts all popup decorations down so the
|
||||
// first visible row of the popup lands at the host window's top edge.
|
||||
// Apply the shift before drawing borders/padding/etc. and restore at
|
||||
// the end of this popup's iteration.
|
||||
wp->w_winrow += wp->w_popup_topoff;
|
||||
|
||||
// Add offset for border and padding if not done already.
|
||||
if ((wp->w_flags & WFLAG_WCOL_OFF_ADDED) == 0)
|
||||
{
|
||||
@@ -5643,8 +6078,22 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
wp->w_flags |= WFLAG_WROW_OFF_ADDED;
|
||||
}
|
||||
|
||||
total_width = popup_width(wp) - wp->w_popup_rightoff;
|
||||
total_height = popup_height(wp);
|
||||
// When clipped by "clipwindow", drop the border/padding slot at the
|
||||
// clipped edge that we will not render, so the popup ends exactly on
|
||||
// the last visible content row (no empty trailing side-border row)
|
||||
// and starts on the first visible row when top-clipped. When
|
||||
// unclipped, fall back to the full popup geometry (cl.eff_width
|
||||
// excludes w_leftcol and the scrollbar, which popup_width() folds in).
|
||||
if (wp->w_popup_leftclip > 0 || wp->w_popup_rightclip > 0)
|
||||
total_width = cl.eff_width;
|
||||
else
|
||||
total_width = popup_width(wp) - wp->w_popup_rightoff;
|
||||
if (total_width < 0)
|
||||
total_width = 0;
|
||||
if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0)
|
||||
total_height = cl.eff_height;
|
||||
else
|
||||
total_height = popup_height(wp);
|
||||
popup_attr = get_win_attr(wp);
|
||||
|
||||
if (wp->w_winrow + total_height > cmdline_row)
|
||||
@@ -5723,16 +6172,16 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr);
|
||||
}
|
||||
|
||||
wincol = wp->w_wincol - wp->w_popup_leftoff;
|
||||
top_padding = wp->w_popup_padding[0];
|
||||
if (wp->w_popup_border[0] > 0)
|
||||
wincol = wp->w_wincol - wp->w_popup_leftoff + wp->w_popup_leftclip;
|
||||
top_padding = cl.eff_padding[0];
|
||||
if (cl.eff_border[0] > 0)
|
||||
{
|
||||
// top border; do not draw over the title
|
||||
if (title_len > 0)
|
||||
{
|
||||
screen_fill(wp->w_winrow, wp->w_winrow + 1,
|
||||
wincol < 0 ? 0 : wincol, title_wincol,
|
||||
wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
? border_char[4] : border_char[0],
|
||||
border_char[0], border_attr[0]);
|
||||
screen_fill(wp->w_winrow, wp->w_winrow + 1,
|
||||
@@ -5743,18 +6192,19 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
{
|
||||
screen_fill(wp->w_winrow, wp->w_winrow + 1,
|
||||
wincol < 0 ? 0 : wincol, wincol + total_width,
|
||||
wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
? border_char[4] : border_char[0],
|
||||
border_char[0], border_attr[0]);
|
||||
}
|
||||
if (wp->w_popup_border[1] > 0)
|
||||
if (cl.eff_border[1] > 0)
|
||||
{
|
||||
buf[mb_char2bytes(border_char[5], buf)] = NUL;
|
||||
screen_puts(buf, wp->w_winrow,
|
||||
wincol + total_width - 1, border_attr[1]);
|
||||
}
|
||||
}
|
||||
else if (wp->w_popup_padding[0] == 0 && popup_top_extra(wp) > 0)
|
||||
else if (cl.eff_padding[0] == 0 && popup_top_extra(wp) > 0
|
||||
&& wp->w_popup_topoff == 0)
|
||||
top_padding = 1;
|
||||
|
||||
if (top_padding > 0 || wp->w_popup_padding[2] > 0)
|
||||
@@ -5844,25 +6294,26 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
attr_thumb = highlight_attr[HLF_PST];
|
||||
}
|
||||
|
||||
for (i = wp->w_popup_border[0];
|
||||
i < total_height - wp->w_popup_border[2]; ++i)
|
||||
// The side-border loop spans the popup's drawn extent. cl.eff_border
|
||||
// and cl.eff_padding collapse the clipped edges to 0 so the loop
|
||||
// covers the full visible area without leaving an empty trailing row.
|
||||
for (i = cl.eff_border[0]; i < total_height - cl.eff_border[2]; ++i)
|
||||
{
|
||||
int pad_left;
|
||||
// left and right padding only needed next to the body
|
||||
int do_padding =
|
||||
i >= wp->w_popup_border[0] + wp->w_popup_padding[0]
|
||||
&& i < total_height - wp->w_popup_border[2]
|
||||
- wp->w_popup_padding[2];
|
||||
i >= cl.eff_border[0] + cl.eff_padding[0]
|
||||
&& i < total_height - cl.eff_border[2] - cl.eff_padding[2];
|
||||
|
||||
row = wp->w_winrow + i;
|
||||
|
||||
// left border
|
||||
if (wp->w_popup_border[3] > 0 && wincol >= 0)
|
||||
if (cl.eff_border[3] > 0 && wincol >= 0)
|
||||
{
|
||||
buf[mb_char2bytes(border_char[3], buf)] = NUL;
|
||||
screen_puts(buf, row, wincol, border_attr[3]);
|
||||
}
|
||||
if (do_padding && wp->w_popup_padding[3] > 0)
|
||||
if (do_padding && cl.eff_padding[3] > 0)
|
||||
{
|
||||
int col = wincol + wp->w_popup_border[3];
|
||||
|
||||
@@ -5899,13 +6350,13 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
screen_putchar(' ', row, scroll_col, popup_attr);
|
||||
}
|
||||
// right border
|
||||
if (wp->w_popup_border[1] > 0)
|
||||
if (cl.eff_border[1] > 0)
|
||||
{
|
||||
buf[mb_char2bytes(border_char[1], buf)] = NUL;
|
||||
screen_puts(buf, row, wincol + total_width - 1, border_attr[1]);
|
||||
}
|
||||
// right padding
|
||||
if (do_padding && wp->w_popup_padding[1] > 0)
|
||||
if (do_padding && cl.eff_padding[1] > 0)
|
||||
{
|
||||
int pad_col_start = wincol + wp->w_popup_border[3]
|
||||
+ wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol;
|
||||
@@ -5932,7 +6383,7 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
}
|
||||
}
|
||||
|
||||
if (wp->w_popup_padding[2] > 0)
|
||||
if (cl.eff_padding[2] > 0)
|
||||
{
|
||||
// bottom padding
|
||||
row = wp->w_winrow + wp->w_popup_border[0]
|
||||
@@ -5945,24 +6396,24 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
padcol, padendcol, ' ', ' ', popup_attr);
|
||||
}
|
||||
|
||||
if (wp->w_popup_border[2] > 0)
|
||||
if (cl.eff_border[2] > 0)
|
||||
{
|
||||
// bottom border
|
||||
row = wp->w_winrow + total_height - 1;
|
||||
screen_fill(row, row + 1,
|
||||
wincol < 0 ? 0 : wincol,
|
||||
wincol + total_width,
|
||||
wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0
|
||||
? border_char[7] : border_char[2],
|
||||
border_char[2], border_attr[2]);
|
||||
if (wp->w_popup_border[1] > 0)
|
||||
if (cl.eff_border[1] > 0)
|
||||
{
|
||||
buf[mb_char2bytes(border_char[6], buf)] = NUL;
|
||||
screen_puts(buf, row, wincol + total_width - 1, border_attr[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wp->w_popup_shadow)
|
||||
if (wp->w_popup_shadow && wp->w_popup_bottomoff == 0)
|
||||
{
|
||||
// bottom shadow
|
||||
row = wp->w_winrow + total_height;
|
||||
@@ -5996,6 +6447,10 @@ update_popups(void (*win_update)(win_T *wp))
|
||||
|
||||
if (override_success)
|
||||
pop_highlight_overrides();
|
||||
|
||||
// Undo the topoff shift applied before drawing the borders so the
|
||||
// next iteration sees the popup's logical winrow.
|
||||
wp->w_winrow -= wp->w_popup_topoff;
|
||||
}
|
||||
|
||||
#ifdef FEAT_PROP_POPUP
|
||||
|
||||
@@ -16,6 +16,7 @@ int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change);
|
||||
int prop_count_above_below(buf_T *buf, linenr_T lnum);
|
||||
int count_props(linenr_T lnum, int only_starting, int last_line);
|
||||
void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count);
|
||||
bool find_prop_in_lines(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum, linenr_T first_lnum, linenr_T last_lnum);
|
||||
bool find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum);
|
||||
char_u *props_add_count_header(char_u *line, int line_len, int textlen, int *new_len);
|
||||
void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count);
|
||||
|
||||
@@ -4197,6 +4197,14 @@ struct window_S
|
||||
|
||||
int w_popup_leftoff; // columns left of the screen
|
||||
int w_popup_rightoff; // columns right of the screen
|
||||
int w_popup_topoff; // rows above the host window's top
|
||||
// when "clipwindow" is set
|
||||
int w_popup_bottomoff; // rows below the host window's bottom
|
||||
// when "clipwindow" is set
|
||||
int w_popup_leftclip; // columns left of the host window's left
|
||||
// when "clipwindow" is set
|
||||
int w_popup_rightclip; // columns right of the host window's right
|
||||
// when "clipwindow" is set
|
||||
varnumber_T w_popup_last_changedtick; // b:changedtick of popup buffer
|
||||
// when position was computed
|
||||
varnumber_T w_popup_prop_changedtick; // b:changedtick of buffer with
|
||||
@@ -4205,6 +4213,14 @@ struct window_S
|
||||
int w_popup_prop_topline; // w_topline of window with
|
||||
// w_popup_prop_type when position was
|
||||
// computed
|
||||
int w_popup_prop_winrow; // w_winrow of host window when
|
||||
// position was computed
|
||||
int w_popup_prop_wincol; // w_wincol of host window when
|
||||
// position was computed
|
||||
int w_popup_prop_width; // w_width of host window when
|
||||
// position was computed
|
||||
int w_popup_prop_winheight; // w_height of host window when
|
||||
// position was computed
|
||||
linenr_T w_popup_last_curline; // last known w_cursor.lnum of window
|
||||
// with "cursorline" set
|
||||
callback_T w_close_cb; // popup close callback
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
>h+0&#ffffff0|o|s|t| |l|i|n|e| |1| @28
|
||||
|h|o|s|t| |l|i|n|e| |2| @28
|
||||
|h|o|s|t| |l|i|n|e| |3| @28
|
||||
|h|o|s|t|╔+0#0000001#e0e0e08|═@8|╗| +0#0000000#ffffff0@24
|
||||
|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |A| |║| +0#0000000#ffffff0@24
|
||||
|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |B| |║| +0#0000000#ffffff0@24
|
||||
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|1|,|1| @11|T|o|p
|
||||
| +0&&@39
|
||||
|~+0#4040ff13&| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1
|
||||
| +0&&@39
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
|h+0&#ffffff0|o|s|t| |l|i|n|e| |4|5| @27
|
||||
|h|o|s|t| |l|i|n|e| |4|6| @27
|
||||
|h|o|s|t| |l|i|n|e| |4|7| @27
|
||||
|h|o|s|t| |l|i|n|e| |4|8| @27
|
||||
|h|o|s|t| |l|i|n|e| |4|9| @27
|
||||
>h|o|s|t| |l|i|n|e| |5|0| @27
|
||||
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|5|0|,|1| @10|B|o|t
|
||||
| +0&&@39
|
||||
|~+0#4040ff13&| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1
|
||||
| +0&&@39
|
||||
@@ -0,0 +1,14 @@
|
||||
| +0&#ffffff0@26||+1&&>h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|═+0#0000001#e0e0e08@7|╗| +0#0000000#ffffff0|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |A|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |B|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |C|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|═+0#0000001#e0e0e08@7|╝| +0#0000000#ffffff0|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d
|
||||
| @31|1|,|1| @10|T|o|p|
|
||||
@@ -0,0 +1,14 @@
|
||||
>h+0&#ffffff0|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&| +0&&@26
|
||||
|h|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|╔+0#0000001#e0e0e08|═@3||+1#0000000#ffffff0|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|╚+0#0000001#e0e0e08|═@3||+1#0000000#ffffff0|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25
|
||||
| +0#0000000&@31|1|,|1| @10|T|o|p|
|
||||
@@ -0,0 +1,14 @@
|
||||
| +0&#ffffff0@39
|
||||
|~+0#4040ff13&| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|~| @38
|
||||
|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1
|
||||
>h+0&&|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |B| |║| +0#0000000#ffffff0@24
|
||||
|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |C| |║| +0#0000000#ffffff0@24
|
||||
|h|o|s|t|╚+0#0000001#e0e0e08|═@8|╝| +0#0000000#ffffff0@24
|
||||
|h|o|s|t| |l|i|n|e| |4| @28
|
||||
|h|o|s|t| |l|i|n|e| |5| @28
|
||||
|h|o|s|t| |l|i|n|e| |6| @28
|
||||
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|1|,|1| @11|T|o|p
|
||||
| +0&&@39
|
||||
@@ -4522,6 +4522,222 @@ func Test_popup_setoptions_other_tab()
|
||||
call prop_type_delete('textprop')
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_option()
|
||||
" Default: clipwindow is off.
|
||||
let id = popup_create('TEST', #{})
|
||||
call assert_equal(0, popup_getoptions(id).clipwindow)
|
||||
call popup_close(id)
|
||||
|
||||
" popup_create() honours the option.
|
||||
let id = popup_create('TEST', #{clipwindow: v:true})
|
||||
call assert_equal(1, popup_getoptions(id).clipwindow)
|
||||
|
||||
" popup_setoptions() can toggle it off and on.
|
||||
call popup_setoptions(id, #{clipwindow: v:false})
|
||||
call assert_equal(0, popup_getoptions(id).clipwindow)
|
||||
call popup_setoptions(id, #{clipwindow: v:true})
|
||||
call assert_equal(1, popup_getoptions(id).clipwindow)
|
||||
|
||||
call popup_close(id)
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_hide_when_prop_off_screen()
|
||||
" A "clipwindow" popup attached to a textprop should be hidden once the
|
||||
" host window scrolls so the textprop is far enough off-screen that even
|
||||
" the partially-clipped popup would no longer overlap, and unhidden again
|
||||
" when the prop scrolls back into reach.
|
||||
call prop_type_add('clipprop', {})
|
||||
new
|
||||
call setline(1, range(1, 200)->mapnew({_, v -> 'line ' .. v}))
|
||||
call prop_add(5, 1, #{type: 'clipprop', length: 5})
|
||||
let host = win_getid()
|
||||
|
||||
let id = popup_create('attached', #{
|
||||
\ textprop: 'clipprop',
|
||||
\ textpropwin: host,
|
||||
\ line: -1,
|
||||
\ wrap: v:false,
|
||||
\ fixed: v:true,
|
||||
\ clipwindow: v:true,
|
||||
\ })
|
||||
redraw
|
||||
call assert_equal(1, popup_getpos(id).visible)
|
||||
|
||||
" Scroll the host so the prop is far below topline; popup hides.
|
||||
call win_execute(host, 'normal! Gzb')
|
||||
redraw
|
||||
call assert_equal(0, popup_getpos(id).visible)
|
||||
|
||||
" Scroll back so the prop is on the first visible line; popup unhides.
|
||||
call win_execute(host, 'normal! ggzt')
|
||||
redraw
|
||||
call assert_equal(1, popup_getpos(id).visible)
|
||||
|
||||
call popup_close(id)
|
||||
bwipe!
|
||||
call prop_type_delete('clipprop')
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_top_clip()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
set nowrap
|
||||
:botright new
|
||||
:resize 6
|
||||
setline(1, range(1, 30)->mapnew((_, v) => 'host line ' .. v))
|
||||
prop_type_add('clipprop', {})
|
||||
prop_add(2, 1, {type: 'clipprop', length: 4})
|
||||
popup_create(['popup A', 'popup B', 'popup C'], {
|
||||
textprop: 'clipprop',
|
||||
line: -4,
|
||||
col: 0,
|
||||
border: [],
|
||||
padding: [0, 1, 0, 1],
|
||||
highlight: 'PmenuSel',
|
||||
wrap: false,
|
||||
fixed: true,
|
||||
posinvert: false,
|
||||
clipwindow: true,
|
||||
})
|
||||
END
|
||||
call writefile(lines, 'XtestPopupClipwindowTop', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupClipwindowTop', #{rows: 14, cols: 40})
|
||||
call VerifyScreenDump(buf, 'Test_popup_clipwindow_top_clip', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_bottom_clip()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
set nowrap
|
||||
:topleft new
|
||||
:resize 6
|
||||
setline(1, range(1, 30)->mapnew((_, v) => 'host line ' .. v))
|
||||
prop_type_add('clipprop', {})
|
||||
prop_add(2, 1, {type: 'clipprop', length: 4})
|
||||
popup_create(['popup A', 'popup B', 'popup C'], {
|
||||
textprop: 'clipprop',
|
||||
line: 1,
|
||||
col: 0,
|
||||
border: [],
|
||||
padding: [0, 1, 0, 1],
|
||||
highlight: 'PmenuSel',
|
||||
wrap: false,
|
||||
fixed: true,
|
||||
posinvert: false,
|
||||
clipwindow: true,
|
||||
})
|
||||
END
|
||||
call writefile(lines, 'XtestPopupClipwindowBottom', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupClipwindowBottom', #{rows: 14, cols: 40})
|
||||
call VerifyScreenDump(buf, 'Test_popup_clipwindow_bottom_clip', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_left_clip()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
set nowrap
|
||||
:vert botright new
|
||||
:vert resize 22
|
||||
set laststatus=0
|
||||
setline(1, repeat(['host content line abcdef'], 20))
|
||||
prop_type_add('clipprop', {})
|
||||
prop_add(5, 6, {type: 'clipprop', length: 4})
|
||||
popup_create(['popup A', 'popup B', 'popup C'], {
|
||||
textprop: 'clipprop',
|
||||
line: 0,
|
||||
col: -10,
|
||||
border: [],
|
||||
padding: [0, 1, 0, 1],
|
||||
highlight: 'PmenuSel',
|
||||
wrap: false,
|
||||
fixed: true,
|
||||
posinvert: false,
|
||||
clipwindow: true,
|
||||
})
|
||||
END
|
||||
call writefile(lines, 'XtestPopupClipwindowLeft', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupClipwindowLeft', #{rows: 14, cols: 50})
|
||||
call VerifyScreenDump(buf, 'Test_popup_clipwindow_left_clip', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_right_clip()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
set nowrap
|
||||
:vert topleft new
|
||||
:vert resize 22
|
||||
set laststatus=0
|
||||
setline(1, repeat(['host content line abcdef'], 20))
|
||||
prop_type_add('clipprop', {})
|
||||
prop_add(5, 14, {type: 'clipprop', length: 4})
|
||||
popup_create(['popup A', 'popup B', 'popup C'], {
|
||||
textprop: 'clipprop',
|
||||
line: 0,
|
||||
col: 0,
|
||||
border: [],
|
||||
padding: [0, 1, 0, 1],
|
||||
highlight: 'PmenuSel',
|
||||
wrap: false,
|
||||
fixed: true,
|
||||
posinvert: false,
|
||||
clipwindow: true,
|
||||
})
|
||||
END
|
||||
call writefile(lines, 'XtestPopupClipwindowRight', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupClipwindowRight', #{rows: 14, cols: 50})
|
||||
call VerifyScreenDump(buf, 'Test_popup_clipwindow_right_clip', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_clipwindow_hidden()
|
||||
CheckScreendump
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
set nowrap
|
||||
:topleft new
|
||||
:resize 6
|
||||
setline(1, range(1, 50)->mapnew((_, v) => 'host line ' .. v))
|
||||
prop_type_add('clipprop', {})
|
||||
prop_add(2, 1, {type: 'clipprop', length: 4})
|
||||
popup_create(['popup A', 'popup B', 'popup C'], {
|
||||
textprop: 'clipprop',
|
||||
line: -4,
|
||||
col: 0,
|
||||
border: [],
|
||||
padding: [0, 1, 0, 1],
|
||||
highlight: 'PmenuSel',
|
||||
wrap: false,
|
||||
fixed: true,
|
||||
posinvert: false,
|
||||
clipwindow: true,
|
||||
})
|
||||
# Scroll the host so the textprop is far below topline; the popup is
|
||||
# then hidden because the prop has scrolled out of the host window.
|
||||
win_execute(win_getid(), 'normal! Gzb')
|
||||
END
|
||||
call writefile(lines, 'XtestPopupClipwindowHidden', 'D')
|
||||
let buf = RunVimInTerminal('-S XtestPopupClipwindowHidden', #{rows: 14, cols: 40})
|
||||
call VerifyScreenDump(buf, 'Test_popup_clipwindow_hidden', {})
|
||||
|
||||
call StopVimInTerminal(buf)
|
||||
endfunc
|
||||
|
||||
func Test_popup_prop_not_visible()
|
||||
CheckScreendump
|
||||
|
||||
|
||||
+31
-9
@@ -1396,25 +1396,28 @@ sort_text_props(
|
||||
}
|
||||
|
||||
/*
|
||||
* Find text property "type_id" in the visible lines of window "wp".
|
||||
* Match "id" when it is > 0.
|
||||
* Returns false when not found.
|
||||
* Find text property "type_id" in lines [first_lnum, last_lnum] of window
|
||||
* "wp"'s buffer. Match "id" when it is > 0. Returns false when not found.
|
||||
*/
|
||||
bool
|
||||
find_visible_prop(
|
||||
find_prop_in_lines(
|
||||
win_T *wp,
|
||||
int type_id,
|
||||
int id,
|
||||
textprop_T *prop,
|
||||
linenr_T *found_lnum)
|
||||
linenr_T *found_lnum,
|
||||
linenr_T first_lnum,
|
||||
linenr_T last_lnum)
|
||||
{
|
||||
// return when "type_id" no longer exists
|
||||
if (text_prop_type_by_id(wp->w_buffer, type_id) == NULL)
|
||||
return false;
|
||||
|
||||
// w_botline may not have been updated yet.
|
||||
validate_botline_win(wp);
|
||||
for (linenr_T lnum = wp->w_topline; lnum < wp->w_botline; ++lnum)
|
||||
if (first_lnum < 1)
|
||||
first_lnum = 1;
|
||||
if (last_lnum > wp->w_buffer->b_ml.ml_line_count)
|
||||
last_lnum = wp->w_buffer->b_ml.ml_line_count;
|
||||
|
||||
for (linenr_T lnum = first_lnum; lnum <= last_lnum; ++lnum)
|
||||
{
|
||||
char_u *props;
|
||||
int count = get_text_props(wp->w_buffer, lnum, &props, FALSE);
|
||||
@@ -1432,6 +1435,25 @@ find_visible_prop(
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find text property "type_id" in the visible lines of window "wp".
|
||||
* Match "id" when it is > 0.
|
||||
* Returns false when not found.
|
||||
*/
|
||||
bool
|
||||
find_visible_prop(
|
||||
win_T *wp,
|
||||
int type_id,
|
||||
int id,
|
||||
textprop_T *prop,
|
||||
linenr_T *found_lnum)
|
||||
{
|
||||
// w_botline may not have been updated yet.
|
||||
validate_botline_win(wp);
|
||||
return find_prop_in_lines(wp, type_id, id, prop, found_lnum,
|
||||
wp->w_topline, wp->w_botline - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the text properties for line "lnum" to "tps" array with "count" entries.
|
||||
* If "count" is zero text properties are removed.
|
||||
|
||||
@@ -729,6 +729,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
469,
|
||||
/**/
|
||||
468,
|
||||
/**/
|
||||
|
||||
@@ -690,6 +690,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
|
||||
#define POPF_INFO_MENU 0x400 // align info popup with popup menu
|
||||
#define POPF_POSINVERT 0x800 // vertical position can be inverted
|
||||
#define POPF_OPACITY 0x1000 // popup has opacity/transparency setting
|
||||
#define POPF_CLIPWINDOW 0x2000 // confine popup to its host window's rect
|
||||
|
||||
// flags used in w_popup_handled
|
||||
#define POPUP_HANDLED_1 0x01 // used by mouse_find_win()
|
||||
|
||||
Reference in New Issue
Block a user