Optionally reuse tooltip frames instead of deleting/recreating them.
* src/frame.c (tooltip_reuse_hidden_frame): New option. * src/w32fns.c (x_create_tip_frame): Remove argument TEXT. Fix handling of dividers. (x_hide_tip): New function. (Fx_show_tip): Try to reuse old tooltip frame when `tooltip-reuse-hidden-frame' is non-nil and frame parameters have not changed. Insert STRING here instead of passing it to x_create_tip_frame. Compute size of tooltip window with Fwindow_text_pixel_size. Obey Vw32_tooltip_extra_pixels when padding tooltip window. (Fx_hide_tip): Call x_hide_tip. (Vw32_tooltip_extra_pixels): New variable. * src/xdisp.c (Fwindow_text_pixel_size): Don't return negative y value. Fix doc-string. * src/xfns.c (x_create_tip_frame): Remove argument TEXT. Call make_frame with mini_p argument false. (x_hide_tip): New function. (Fx_show_tip): Try to reuse old tooltip frame when `tooltip-reuse-hidden-frame' is non-nil and frame parameters have not changed. Insert STRING here instead of passing it to x_create_tip_frame. Compute size of tooltip window with Fwindow_text_pixel_size. (Fx_hide_tip): Call x_hide_tip. * lisp/cus-start.el (tooltip-reuse-hidden-frame): Add customization entry.
This commit is contained in:
parent
59c7a5d711
commit
80864c2a04
5 changed files with 470 additions and 485 deletions
|
@ -311,6 +311,7 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
|
|||
(const :tag "Always" t)
|
||||
(repeat (symbol :tag "Parameter")))
|
||||
"25.1")
|
||||
(tooltip-reuse-hidden-frame tooltip boolean "26.1")
|
||||
;; fringe.c
|
||||
(overflow-newline-into-fringe fringe boolean)
|
||||
;; image.c
|
||||
|
|
15
src/frame.c
15
src/frame.c
|
@ -5262,6 +5262,21 @@ The function `frame--size-history' displays the value of this variable
|
|||
in a more readable form. */);
|
||||
frame_size_history = Qnil;
|
||||
|
||||
DEFVAR_BOOL ("tooltip-reuse-hidden-frame", tooltip_reuse_hidden_frame,
|
||||
doc: /* Non-nil means reuse hidden tooltip frames.
|
||||
When this is nil, delete a tooltip frame when hiding the associated
|
||||
tooltip. When this is non-nil, make the tooltip frame invisible only,
|
||||
so it can be reused when the next tooltip is shown.
|
||||
|
||||
Setting this to non-nil may drastically reduce the consing overhead
|
||||
incurred by creating new tooltip frames. However, a value of non-nil
|
||||
means also that intermittent changes of faces or `default-frame-alist'
|
||||
are not applied when showing a tooltip in a reused frame.
|
||||
|
||||
This variable is effective only with the X toolkit (and there only when
|
||||
Gtk+ tooltips are not used) and on Windows. */);
|
||||
tooltip_reuse_hidden_frame = false;
|
||||
|
||||
staticpro (&Vframe_list);
|
||||
|
||||
defsubr (&Sframep);
|
||||
|
|
435
src/w32fns.c
435
src/w32fns.c
|
@ -6422,8 +6422,6 @@ no value of TYPE (always string in the MS Windows case). */)
|
|||
Tool tips
|
||||
***********************************************************************/
|
||||
|
||||
static Lisp_Object x_create_tip_frame (struct w32_display_info *,
|
||||
Lisp_Object, Lisp_Object);
|
||||
static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
|
||||
Lisp_Object, int, int, int *, int *);
|
||||
|
||||
|
@ -6458,8 +6456,7 @@ unwind_create_tip_frame (Lisp_Object frame)
|
|||
|
||||
|
||||
/* Create a frame for a tooltip on the display described by DPYINFO.
|
||||
PARMS is a list of frame parameters. TEXT is the string to
|
||||
display in the tip frame. Value is the frame.
|
||||
PARMS is a list of frame parameters. Value is the frame.
|
||||
|
||||
Note that functions called here, esp. x_default_parameter can
|
||||
signal errors, for instance when a specified color name is
|
||||
|
@ -6467,8 +6464,7 @@ unwind_create_tip_frame (Lisp_Object frame)
|
|||
when this happens. */
|
||||
|
||||
static Lisp_Object
|
||||
x_create_tip_frame (struct w32_display_info *dpyinfo,
|
||||
Lisp_Object parms, Lisp_Object text)
|
||||
x_create_tip_frame (struct w32_display_info *dpyinfo, Lisp_Object parms)
|
||||
{
|
||||
struct frame *f;
|
||||
Lisp_Object frame;
|
||||
|
@ -6477,8 +6473,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
struct kboard *kb;
|
||||
bool face_change_before = face_change;
|
||||
Lisp_Object buffer;
|
||||
struct buffer *old_buffer;
|
||||
int x_width = 0, x_height = 0;
|
||||
|
||||
/* Use this general default value to start with until we know if
|
||||
|
@ -6502,23 +6496,9 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
frame = Qnil;
|
||||
/* Make a frame without minibuffer nor mode-line. */
|
||||
f = make_frame (false);
|
||||
f->wants_modeline = 0;
|
||||
f->wants_modeline = false;
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
AUTO_STRING (tip, " *tip*");
|
||||
buffer = Fget_buffer_create (tip);
|
||||
/* Use set_window_buffer instead of Fset_window_buffer (see
|
||||
discussion of bug#11984, bug#12025, bug#12026). */
|
||||
set_window_buffer (FRAME_ROOT_WINDOW (f), buffer, false, false);
|
||||
old_buffer = current_buffer;
|
||||
set_buffer_internal_1 (XBUFFER (buffer));
|
||||
bset_truncate_lines (current_buffer, Qnil);
|
||||
specbind (Qinhibit_read_only, Qt);
|
||||
specbind (Qinhibit_modification_hooks, Qt);
|
||||
Ferase_buffer ();
|
||||
Finsert (1, &text);
|
||||
set_buffer_internal_1 (old_buffer);
|
||||
|
||||
record_unwind_protect (unwind_create_tip_frame, frame);
|
||||
|
||||
/* By setting the output method, we're essentially saying that
|
||||
|
@ -6552,7 +6532,7 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
{
|
||||
fset_name (f, name);
|
||||
f->explicit_name = true;
|
||||
/* use the frame's title when getting resources for this frame. */
|
||||
/* Use the frame's title when getting resources for this frame. */
|
||||
specbind (Qx_resource_name, name);
|
||||
}
|
||||
|
||||
|
@ -6582,14 +6562,10 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
parms = Fcons (Fcons (Qinternal_border_width, value),
|
||||
parms);
|
||||
}
|
||||
|
||||
x_default_parameter (f, parms, Qinternal_border_width, make_number (1),
|
||||
"internalBorderWidth", "internalBorderWidth",
|
||||
RES_TYPE_NUMBER);
|
||||
x_default_parameter (f, parms, Qright_divider_width, make_number (0),
|
||||
NULL, NULL, RES_TYPE_NUMBER);
|
||||
x_default_parameter (f, parms, Qbottom_divider_width, make_number (0),
|
||||
NULL, NULL, RES_TYPE_NUMBER);
|
||||
|
||||
/* Also do the stuff which must be set before the window exists. */
|
||||
x_default_parameter (f, parms, Qforeground_color, build_string ("black"),
|
||||
"foreground", "Foreground", RES_TYPE_STRING);
|
||||
|
@ -6616,6 +6592,9 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
f->fringe_cols = 0;
|
||||
f->left_fringe_width = 0;
|
||||
f->right_fringe_width = 0;
|
||||
/* No dividers on tip frame. */
|
||||
f->right_divider_width = 0;
|
||||
f->bottom_divider_width = 0;
|
||||
|
||||
block_input ();
|
||||
my_create_tip_window (f);
|
||||
|
@ -6642,7 +6621,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
SET_FRAME_LINES (f, 0);
|
||||
adjust_frame_size (f, width * FRAME_COLUMN_WIDTH (f),
|
||||
height * FRAME_LINE_HEIGHT (f), 0, true, Qtip_frame);
|
||||
|
||||
/* Add `tooltip' frame parameter's default value. */
|
||||
if (NILP (Fframe_parameter (frame, Qtooltip)))
|
||||
Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil));
|
||||
|
@ -6660,8 +6638,6 @@ x_create_tip_frame (struct w32_display_info *dpyinfo,
|
|||
Lisp_Object fg = Fframe_parameter (frame, Qforeground_color);
|
||||
Lisp_Object colors = Qnil;
|
||||
|
||||
/* Set tip_frame here, so that */
|
||||
tip_frame = frame;
|
||||
call2 (Qface_set_after_frame_default, frame, Qnil);
|
||||
|
||||
if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
|
||||
|
@ -6793,6 +6769,48 @@ compute_tip_xy (struct frame *f,
|
|||
*root_x = min_x;
|
||||
}
|
||||
|
||||
/* Hide tooltip. Delete its frame if DELETE is true. */
|
||||
static Lisp_Object
|
||||
x_hide_tip (bool delete)
|
||||
{
|
||||
if (!NILP (tip_timer))
|
||||
{
|
||||
call1 (Qcancel_timer, tip_timer);
|
||||
tip_timer = Qnil;
|
||||
}
|
||||
|
||||
if (NILP (tip_frame)
|
||||
|| (!delete && FRAMEP (tip_frame)
|
||||
&& !FRAME_VISIBLE_P (XFRAME (tip_frame))))
|
||||
return Qnil;
|
||||
else
|
||||
{
|
||||
ptrdiff_t count;
|
||||
Lisp_Object was_open = Qnil;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
if (FRAMEP (tip_frame))
|
||||
{
|
||||
if (delete)
|
||||
{
|
||||
delete_frame (tip_frame, Qnil);
|
||||
tip_frame = Qnil;
|
||||
}
|
||||
else
|
||||
x_make_frame_invisible (XFRAME (tip_frame));
|
||||
|
||||
was_open = Qt;
|
||||
}
|
||||
else
|
||||
tip_frame = Qnil;
|
||||
|
||||
return unbind_to (count, was_open);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
|
||||
doc: /* Show STRING in a \"tooltip\" window on frame FRAME.
|
||||
|
@ -6826,15 +6844,16 @@ A tooltip's maximum size is specified by `x-max-tooltip-size'.
|
|||
Text larger than the specified size is clipped. */)
|
||||
(Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
|
||||
{
|
||||
struct frame *f;
|
||||
struct frame *f, *tip_f;
|
||||
struct window *w;
|
||||
int root_x, root_y;
|
||||
struct buffer *old_buffer;
|
||||
struct text_pos pos;
|
||||
int i, width, height;
|
||||
bool seen_reversed_p;
|
||||
int old_windows_or_buffers_changed = windows_or_buffers_changed;
|
||||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
ptrdiff_t count_1;
|
||||
Lisp_Object window, size;
|
||||
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
|
||||
|
@ -6858,91 +6877,155 @@ Text larger than the specified size is clipped. */)
|
|||
if (NILP (last_show_tip_args))
|
||||
last_show_tip_args = Fmake_vector (make_number (3), Qnil);
|
||||
|
||||
if (!NILP (tip_frame))
|
||||
if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
|
||||
{
|
||||
Lisp_Object last_string = AREF (last_show_tip_args, 0);
|
||||
Lisp_Object last_frame = AREF (last_show_tip_args, 1);
|
||||
Lisp_Object last_parms = AREF (last_show_tip_args, 2);
|
||||
|
||||
if (EQ (frame, last_frame)
|
||||
&& !NILP (Fequal (last_string, string))
|
||||
if (FRAME_VISIBLE_P (XFRAME (tip_frame))
|
||||
&& EQ (frame, last_frame)
|
||||
&& !NILP (Fequal_including_properties (last_string, string))
|
||||
&& !NILP (Fequal (last_parms, parms)))
|
||||
{
|
||||
struct frame *f = XFRAME (tip_frame);
|
||||
|
||||
/* Only DX and DY have changed. */
|
||||
tip_f = XFRAME (tip_frame);
|
||||
if (!NILP (tip_timer))
|
||||
{
|
||||
Lisp_Object timer = tip_timer;
|
||||
|
||||
tip_timer = Qnil;
|
||||
call1 (Qcancel_timer, timer);
|
||||
}
|
||||
|
||||
block_input ();
|
||||
compute_tip_xy (f, parms, dx, dy, FRAME_PIXEL_WIDTH (f),
|
||||
FRAME_PIXEL_HEIGHT (f), &root_x, &root_y);
|
||||
compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
|
||||
FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
|
||||
|
||||
/* Put tooltip in topmost group and in position. */
|
||||
SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST,
|
||||
SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST,
|
||||
root_x, root_y, 0, 0,
|
||||
SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
||||
|
||||
/* Ensure tooltip is on top of other topmost windows (eg menus). */
|
||||
SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP,
|
||||
SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP,
|
||||
0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE
|
||||
| SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
||||
|
||||
/* Let redisplay know that we have made the frame visible already. */
|
||||
SET_FRAME_VISIBLE (tip_f, 1);
|
||||
ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE);
|
||||
unblock_input ();
|
||||
|
||||
goto start_timer;
|
||||
}
|
||||
}
|
||||
else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame))
|
||||
{
|
||||
bool delete = false;
|
||||
Lisp_Object tail, elt, parm, last;
|
||||
|
||||
/* Hide a previous tip, if any. */
|
||||
Fx_hide_tip ();
|
||||
/* Check if every parameter in PARMS has the same value in
|
||||
last_parms. This may destruct last_parms which, however,
|
||||
will be recreated below. */
|
||||
for (tail = parms; CONSP (tail); tail = XCDR (tail))
|
||||
{
|
||||
elt = XCAR (tail);
|
||||
parm = Fcar (elt);
|
||||
/* The left, top, right and bottom parameters are handled
|
||||
by compute_tip_xy so they can be ignored here. */
|
||||
if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
|
||||
&& !EQ (parm, Qright) && !EQ (parm, Qbottom))
|
||||
{
|
||||
last = Fassq (parm, last_parms);
|
||||
if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
|
||||
{
|
||||
/* We lost, delete the old tooltip. */
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
last_parms = call2 (Qassq_delete_all, parm, last_parms);
|
||||
}
|
||||
else
|
||||
last_parms = call2 (Qassq_delete_all, parm, last_parms);
|
||||
}
|
||||
|
||||
/* Now check if there's a parameter left in last_parms with a
|
||||
non-nil value. */
|
||||
for (tail = last_parms; CONSP (tail); tail = XCDR (tail))
|
||||
{
|
||||
elt = XCAR (tail);
|
||||
parm = Fcar (elt);
|
||||
if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
|
||||
&& !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
|
||||
{
|
||||
/* We lost, delete the old tooltip. */
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
x_hide_tip (delete);
|
||||
}
|
||||
else
|
||||
x_hide_tip (true);
|
||||
}
|
||||
else
|
||||
x_hide_tip (true);
|
||||
|
||||
ASET (last_show_tip_args, 0, string);
|
||||
ASET (last_show_tip_args, 1, frame);
|
||||
ASET (last_show_tip_args, 2, parms);
|
||||
|
||||
/* Add default values to frame parameters. */
|
||||
if (NILP (Fassq (Qname, parms)))
|
||||
parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
|
||||
if (NILP (Fassq (Qinternal_border_width, parms)))
|
||||
parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms);
|
||||
if (NILP (Fassq (Qright_divider_width, parms)))
|
||||
parms = Fcons (Fcons (Qright_divider_width, make_number (0)), parms);
|
||||
if (NILP (Fassq (Qbottom_divider_width, parms)))
|
||||
parms = Fcons (Fcons (Qbottom_divider_width, make_number (0)), parms);
|
||||
if (NILP (Fassq (Qborder_width, parms)))
|
||||
parms = Fcons (Fcons (Qborder_width, make_number (1)), parms);
|
||||
if (NILP (Fassq (Qborder_color, parms)))
|
||||
parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms);
|
||||
if (NILP (Fassq (Qbackground_color, parms)))
|
||||
parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
|
||||
parms);
|
||||
|
||||
/* Block input until the tip has been fully drawn, to avoid crashes
|
||||
when drawing tips in menus. */
|
||||
block_input ();
|
||||
|
||||
/* Create a frame for the tooltip, and record it in the global
|
||||
variable tip_frame. */
|
||||
frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, string);
|
||||
f = XFRAME (frame);
|
||||
if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
|
||||
{
|
||||
/* Add default values to frame parameters. */
|
||||
if (NILP (Fassq (Qname, parms)))
|
||||
parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
|
||||
if (NILP (Fassq (Qinternal_border_width, parms)))
|
||||
parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms);
|
||||
if (NILP (Fassq (Qborder_width, parms)))
|
||||
parms = Fcons (Fcons (Qborder_width, make_number (1)), parms);
|
||||
if (NILP (Fassq (Qborder_color, parms)))
|
||||
parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms);
|
||||
if (NILP (Fassq (Qbackground_color, parms)))
|
||||
parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
|
||||
parms);
|
||||
|
||||
/* Set up the frame's root window. */
|
||||
w = XWINDOW (FRAME_ROOT_WINDOW (f));
|
||||
/* Create a frame for the tooltip, and record it in the global
|
||||
variable tip_frame. */
|
||||
if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms)))
|
||||
{
|
||||
/* Creating the tip frame failed. */
|
||||
unblock_input ();
|
||||
return unbind_to (count, Qnil);
|
||||
}
|
||||
}
|
||||
|
||||
tip_f = XFRAME (tip_frame);
|
||||
window = FRAME_ROOT_WINDOW (tip_f);
|
||||
AUTO_STRING (tip, " *tip*");
|
||||
set_window_buffer (window, Fget_buffer_create (tip), false, false);
|
||||
w = XWINDOW (window);
|
||||
w->pseudo_window_p = true;
|
||||
|
||||
/* Set up the frame's root window. Note: The following code does not
|
||||
try to size the window or its frame correctly. Its only purpose is
|
||||
to make the subsequent text size calculations work. The right
|
||||
sizes should get installed when the toolkit gets back to us. */
|
||||
w->left_col = 0;
|
||||
w->top_line = 0;
|
||||
w->pixel_left = 0;
|
||||
w->pixel_top = 0;
|
||||
|
||||
if (CONSP (Vx_max_tooltip_size)
|
||||
&& INTEGERP (XCAR (Vx_max_tooltip_size))
|
||||
&& XINT (XCAR (Vx_max_tooltip_size)) > 0
|
||||
&& INTEGERP (XCDR (Vx_max_tooltip_size))
|
||||
&& XINT (XCDR (Vx_max_tooltip_size)) > 0)
|
||||
&& RANGED_INTEGERP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
|
||||
&& RANGED_INTEGERP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
|
||||
{
|
||||
w->total_cols = XFASTINT (XCAR (Vx_max_tooltip_size));
|
||||
w->total_lines = XFASTINT (XCDR (Vx_max_tooltip_size));
|
||||
|
@ -6953,164 +7036,71 @@ Text larger than the specified size is clipped. */)
|
|||
w->total_lines = 40;
|
||||
}
|
||||
|
||||
w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (f);
|
||||
w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (f);
|
||||
w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
|
||||
w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
|
||||
FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w);
|
||||
adjust_frame_glyphs (tip_f);
|
||||
|
||||
FRAME_TOTAL_COLS (f) = WINDOW_TOTAL_COLS (w);
|
||||
adjust_frame_glyphs (f);
|
||||
w->pseudo_window_p = true;
|
||||
|
||||
/* Display the tooltip text in a temporary buffer. */
|
||||
/* Insert STRING into the root window's buffer and fit the frame to
|
||||
the buffer. */
|
||||
count_1 = SPECPDL_INDEX ();
|
||||
old_buffer = current_buffer;
|
||||
set_buffer_internal_1 (XBUFFER (XWINDOW (FRAME_ROOT_WINDOW (f))->contents));
|
||||
set_buffer_internal_1 (XBUFFER (w->contents));
|
||||
bset_truncate_lines (current_buffer, Qnil);
|
||||
specbind (Qinhibit_read_only, Qt);
|
||||
specbind (Qinhibit_modification_hooks, Qt);
|
||||
specbind (Qinhibit_point_motion_hooks, Qt);
|
||||
Ferase_buffer ();
|
||||
Finsert (1, &string);
|
||||
clear_glyph_matrix (w->desired_matrix);
|
||||
clear_glyph_matrix (w->current_matrix);
|
||||
SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
|
||||
try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
|
||||
|
||||
/* Compute width and height of the tooltip. */
|
||||
width = height = 0;
|
||||
seen_reversed_p = false;
|
||||
for (i = 0; i < w->desired_matrix->nrows; ++i)
|
||||
{
|
||||
struct glyph_row *row = &w->desired_matrix->rows[i];
|
||||
struct glyph *last;
|
||||
int row_width;
|
||||
|
||||
/* Stop at the first empty row at the end. */
|
||||
if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row))
|
||||
break;
|
||||
|
||||
/* Let the row go over the full width of the frame. */
|
||||
row->full_width_p = true;
|
||||
|
||||
row_width = row->pixel_width;
|
||||
if (row->used[TEXT_AREA])
|
||||
{
|
||||
if (!row->reversed_p)
|
||||
{
|
||||
/* There's a glyph at the end of rows that is used to
|
||||
place the cursor there. Don't include the width of
|
||||
this glyph. */
|
||||
last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1];
|
||||
if (NILP (last->object))
|
||||
row_width -= last->pixel_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There could be a stretch glyph at the beginning of R2L
|
||||
rows that is produced by extend_face_to_end_of_line.
|
||||
Don't count that glyph. */
|
||||
struct glyph *g = row->glyphs[TEXT_AREA];
|
||||
|
||||
if (g->type == STRETCH_GLYPH && NILP (g->object))
|
||||
{
|
||||
row_width -= g->pixel_width;
|
||||
seen_reversed_p = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
height += row->height;
|
||||
width = max (width, row_width);
|
||||
}
|
||||
|
||||
/* If we've seen partial-length R2L rows, we need to re-adjust the
|
||||
tool-tip frame width and redisplay it again, to avoid over-wide
|
||||
tips due to the stretch glyph that extends R2L lines to full
|
||||
width of the frame. */
|
||||
if (seen_reversed_p)
|
||||
{
|
||||
/* PXW: Why do we do the pixel-to-cols conversion only if
|
||||
seen_reversed_p holds? Don't we have to set other fields of
|
||||
the window/frame structure?
|
||||
|
||||
w->total_cols and FRAME_TOTAL_COLS want the width in columns,
|
||||
not in pixels. */
|
||||
w->pixel_width = width;
|
||||
width /= WINDOW_FRAME_COLUMN_WIDTH (w);
|
||||
w->total_cols = width;
|
||||
FRAME_TOTAL_COLS (f) = width;
|
||||
SET_FRAME_WIDTH (f, width);
|
||||
adjust_frame_glyphs (f);
|
||||
w->pseudo_window_p = 1;
|
||||
clear_glyph_matrix (w->desired_matrix);
|
||||
clear_glyph_matrix (w->current_matrix);
|
||||
try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
|
||||
width = height = 0;
|
||||
/* Recompute width and height of the tooltip. */
|
||||
for (i = 0; i < w->desired_matrix->nrows; ++i)
|
||||
{
|
||||
struct glyph_row *row = &w->desired_matrix->rows[i];
|
||||
struct glyph *last;
|
||||
int row_width;
|
||||
|
||||
if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row))
|
||||
break;
|
||||
row->full_width_p = true;
|
||||
row_width = row->pixel_width;
|
||||
if (row->used[TEXT_AREA] && !row->reversed_p)
|
||||
{
|
||||
last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1];
|
||||
if (NILP (last->object))
|
||||
row_width -= last->pixel_width;
|
||||
}
|
||||
|
||||
height += row->height;
|
||||
width = max (width, row_width);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the frame's internal border to the width and height the w32
|
||||
window should have. */
|
||||
height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
|
||||
width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
|
||||
|
||||
/* Move the tooltip window where the mouse pointer is. Resize and
|
||||
show it.
|
||||
|
||||
PXW: This should use the frame's pixel coordinates. */
|
||||
compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y);
|
||||
try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
|
||||
/* Calculate size of tooltip window. */
|
||||
size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
|
||||
make_number (w->pixel_height), Qnil);
|
||||
/* Add the frame's internal border to calculated size. */
|
||||
width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
|
||||
height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
|
||||
/* Calculate position of tooltip frame. */
|
||||
compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
|
||||
|
||||
/* Show tooltip frame. */
|
||||
{
|
||||
/* Adjust Window size to take border into account. */
|
||||
RECT rect;
|
||||
int pad = (NUMBERP (Vw32_tooltip_extra_pixels)
|
||||
? max (0, XINT (Vw32_tooltip_extra_pixels))
|
||||
: FRAME_COLUMN_WIDTH (tip_f));
|
||||
|
||||
rect.left = rect.top = 0;
|
||||
rect.right = width;
|
||||
rect.bottom = height;
|
||||
AdjustWindowRect (&rect, f->output_data.w32->dwStyle, false);
|
||||
AdjustWindowRect (&rect, tip_f->output_data.w32->dwStyle,
|
||||
FRAME_EXTERNAL_MENU_BAR (tip_f));
|
||||
|
||||
/* Position and size tooltip, and put it in the topmost group.
|
||||
The add-on of FRAME_COLUMN_WIDTH to the 5th argument is a
|
||||
peculiarity of w32 display: without it, some fonts cause the
|
||||
last character of the tip to be truncated or wrapped around to
|
||||
the next line. */
|
||||
SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOPMOST,
|
||||
/* Position and size tooltip and put it in the topmost group. */
|
||||
SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOPMOST,
|
||||
root_x, root_y,
|
||||
rect.right - rect.left + FRAME_COLUMN_WIDTH (f),
|
||||
rect.right - rect.left + pad,
|
||||
rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
||||
|
||||
/* Ensure tooltip is on top of other topmost windows (eg menus). */
|
||||
SetWindowPos (FRAME_W32_WINDOW (f), HWND_TOP,
|
||||
SetWindowPos (FRAME_W32_WINDOW (tip_f), HWND_TOP,
|
||||
0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE
|
||||
| SWP_NOACTIVATE | SWP_NOOWNERZORDER);
|
||||
|
||||
/* Let redisplay know that we have made the frame visible already. */
|
||||
SET_FRAME_VISIBLE (f, 1);
|
||||
SET_FRAME_VISIBLE (tip_f, 1);
|
||||
|
||||
ShowWindow (FRAME_W32_WINDOW (f), SW_SHOWNOACTIVATE);
|
||||
ShowWindow (FRAME_W32_WINDOW (tip_f), SW_SHOWNOACTIVATE);
|
||||
}
|
||||
|
||||
/* Draw into the window. */
|
||||
w->must_be_updated_p = true;
|
||||
update_single_window (w);
|
||||
|
||||
unblock_input ();
|
||||
|
||||
/* Restore original current buffer. */
|
||||
set_buffer_internal_1 (old_buffer);
|
||||
unbind_to (count_1, Qnil);
|
||||
unblock_input ();
|
||||
windows_or_buffers_changed = old_windows_or_buffers_changed;
|
||||
|
||||
start_timer:
|
||||
|
@ -7127,31 +7117,7 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
|
|||
Value is t if tooltip was open, nil otherwise. */)
|
||||
(void)
|
||||
{
|
||||
ptrdiff_t count;
|
||||
Lisp_Object deleted, frame, timer;
|
||||
|
||||
/* Return quickly if nothing to do. */
|
||||
if (NILP (tip_timer) && NILP (tip_frame))
|
||||
return Qnil;
|
||||
|
||||
frame = tip_frame;
|
||||
timer = tip_timer;
|
||||
tip_frame = tip_timer = deleted = Qnil;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
if (!NILP (timer))
|
||||
call1 (Qcancel_timer, timer);
|
||||
|
||||
if (FRAMEP (frame))
|
||||
{
|
||||
delete_frame (frame, Qnil);
|
||||
deleted = Qt;
|
||||
}
|
||||
|
||||
return unbind_to (count, deleted);
|
||||
return x_hide_tip (!tooltip_reuse_hidden_frame);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -9751,6 +9717,7 @@ syms_of_w32fns (void)
|
|||
DEFSYM (Qmm_size, "mm-size");
|
||||
DEFSYM (Qframes, "frames");
|
||||
DEFSYM (Qtip_frame, "tip-frame");
|
||||
DEFSYM (Qassq_delete_all, "assq-delete-all");
|
||||
DEFSYM (Qunicode_sip, "unicode-sip");
|
||||
#if defined WINDOWSNT && !defined HAVE_DBUS
|
||||
DEFSYM (QCicon, ":icon");
|
||||
|
@ -10063,6 +10030,18 @@ Default is nil.
|
|||
This variable has effect only on Windows Vista and later. */);
|
||||
w32_disable_new_uniscribe_apis = 0;
|
||||
|
||||
DEFVAR_LISP ("w32-tooltip-extra-pixels",
|
||||
Vw32_tooltip_extra_pixels,
|
||||
doc: /* Number of pixels added after tooltip text.
|
||||
On Windows some fonts may cause the last character of a tooltip be
|
||||
truncated or wrapped around to the next line. Adding some extra space
|
||||
at the end of the toooltip works around this problem.
|
||||
|
||||
This variable specifies the number of pixels that shall be added. The
|
||||
default value t means to add the width of one canonical character of the
|
||||
tip frame. */);
|
||||
Vw32_tooltip_extra_pixels = Qt;
|
||||
|
||||
#if 0 /* TODO: Port to W32 */
|
||||
defsubr (&Sx_change_window_property);
|
||||
defsubr (&Sx_delete_window_property);
|
||||
|
|
73
src/xdisp.c
73
src/xdisp.c
|
@ -9794,26 +9794,28 @@ the maximum pixel-height of all text lines.
|
|||
|
||||
The optional argument FROM, if non-nil, specifies the first text
|
||||
position and defaults to the minimum accessible position of the buffer.
|
||||
If FROM is t, use the minimum accessible position that is not a newline
|
||||
character. TO, if non-nil, specifies the last text position and
|
||||
If FROM is t, use the minimum accessible position that starts a
|
||||
non-empty line. TO, if non-nil, specifies the last text position and
|
||||
defaults to the maximum accessible position of the buffer. If TO is t,
|
||||
use the maximum accessible position that is not a newline character.
|
||||
use the maximum accessible position that ends a non-empty line.
|
||||
|
||||
The optional argument X-LIMIT, if non-nil, specifies the maximum text
|
||||
width that can be returned. X-LIMIT nil or omitted, means to use the
|
||||
pixel-width of WINDOW's body; use this if you do not intend to change
|
||||
the width of WINDOW. Use the maximum width WINDOW may assume if you
|
||||
intend to change WINDOW's width. In any case, text whose x-coordinate
|
||||
is beyond X-LIMIT is ignored. Since calculating the width of long lines
|
||||
can take some time, it's always a good idea to make this argument as
|
||||
small as possible; in particular, if the buffer contains long lines that
|
||||
shall be truncated anyway.
|
||||
pixel-width of WINDOW's body; use this if you want to know how high
|
||||
WINDOW should be become in order to fit all of its buffer's text with
|
||||
the width of WINDOW unaltered. Use the maximum width WINDOW may assume
|
||||
if you intend to change WINDOW's width. In any case, text whose
|
||||
x-coordinate is beyond X-LIMIT is ignored. Since calculating the width
|
||||
of long lines can take some time, it's always a good idea to make this
|
||||
argument as small as possible; in particular, if the buffer contains
|
||||
long lines that shall be truncated anyway.
|
||||
|
||||
The optional argument Y-LIMIT, if non-nil, specifies the maximum text
|
||||
height that can be returned. Text lines whose y-coordinate is beyond
|
||||
Y-LIMIT are ignored. Since calculating the text height of a large
|
||||
buffer can take some time, it makes sense to specify this argument if
|
||||
the size of the buffer is unknown.
|
||||
height (exluding the height of the mode- or header-line, if any) that
|
||||
can be returned. Text lines whose y-coordinate is beyond Y-LIMIT are
|
||||
ignored. Since calculating the text height of a large buffer can take
|
||||
some time, it makes sense to specify this argument if the size of the
|
||||
buffer is large or unknown.
|
||||
|
||||
Optional argument MODE-AND-HEADER-LINE nil or omitted means do not
|
||||
include the height of the mode- or header-line of WINDOW in the return
|
||||
|
@ -9831,7 +9833,7 @@ include the height of both, if present, in the return value. */)
|
|||
ptrdiff_t start, end, pos;
|
||||
struct text_pos startp;
|
||||
void *itdata = NULL;
|
||||
int c, max_y = -1, x = 0, y = 0;
|
||||
int c, max_x = 0, max_y = 0, x = 0, y = 0;
|
||||
|
||||
CHECK_BUFFER (buffer);
|
||||
b = XBUFFER (buffer);
|
||||
|
@ -9876,11 +9878,13 @@ include the height of both, if present, in the return value. */)
|
|||
end = max (start, min (XINT (to), ZV));
|
||||
}
|
||||
|
||||
if (!NILP (y_limit))
|
||||
{
|
||||
CHECK_NUMBER (y_limit);
|
||||
max_y = min (XINT (y_limit), INT_MAX);
|
||||
}
|
||||
if (!NILP (x_limit) && RANGED_INTEGERP (0, x_limit, INT_MAX))
|
||||
max_x = XINT (x_limit);
|
||||
|
||||
if (NILP (y_limit))
|
||||
max_y = INT_MAX;
|
||||
else if (RANGED_INTEGERP (0, y_limit, INT_MAX))
|
||||
max_y = XINT (y_limit);
|
||||
|
||||
itdata = bidi_shelve_cache ();
|
||||
SET_TEXT_POS (startp, start, CHAR_TO_BYTE (start));
|
||||
|
@ -9890,27 +9894,30 @@ include the height of both, if present, in the return value. */)
|
|||
x = move_it_to (&it, end, -1, max_y, -1, MOVE_TO_POS | MOVE_TO_Y);
|
||||
else
|
||||
{
|
||||
CHECK_NUMBER (x_limit);
|
||||
it.last_visible_x = min (XINT (x_limit), INFINITY);
|
||||
it.last_visible_x = max_x;
|
||||
/* Actually, we never want move_it_to stop at to_x. But to make
|
||||
sure that move_it_in_display_line_to always moves far enough,
|
||||
we set it to INT_MAX and specify MOVE_TO_X. */
|
||||
x = move_it_to (&it, end, INT_MAX, max_y, -1,
|
||||
MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
|
||||
we set it to INT_MAX and specify MOVE_TO_X. Also bound width
|
||||
value by X-LIMIT. */
|
||||
x = min (move_it_to (&it, end, INT_MAX, max_y, -1,
|
||||
MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y),
|
||||
max_x);
|
||||
}
|
||||
|
||||
y = it.current_y + it.max_ascent + it.max_descent;
|
||||
/* Subtract height of header-line which was counted automatically by
|
||||
start_display. */
|
||||
y = min (it.current_y + it.max_ascent + it.max_descent
|
||||
- WINDOW_HEADER_LINE_HEIGHT (w),
|
||||
max_y);
|
||||
|
||||
if (!EQ (mode_and_header_line, Qheader_line)
|
||||
&& !EQ (mode_and_header_line, Qt))
|
||||
/* Do not count the header-line which was counted automatically by
|
||||
start_display. */
|
||||
y = y - WINDOW_HEADER_LINE_HEIGHT (w);
|
||||
if (EQ (mode_and_header_line, Qheader_line)
|
||||
|| EQ (mode_and_header_line, Qt))
|
||||
/* Re-add height of header-line as requested. */
|
||||
y = y + WINDOW_HEADER_LINE_HEIGHT (w);
|
||||
|
||||
if (EQ (mode_and_header_line, Qmode_line)
|
||||
|| EQ (mode_and_header_line, Qt))
|
||||
/* Do count the mode-line which is not included automatically by
|
||||
start_display. */
|
||||
/* Add height of mode-line as requested. */
|
||||
y = y + WINDOW_MODE_LINE_HEIGHT (w);
|
||||
|
||||
bidi_unshelve_cache (itdata, false);
|
||||
|
|
431
src/xfns.c
431
src/xfns.c
|
@ -5303,8 +5303,6 @@ no value of TYPE (always string in the MS Windows case). */)
|
|||
Tool tips
|
||||
***********************************************************************/
|
||||
|
||||
static Lisp_Object x_create_tip_frame (struct x_display_info *,
|
||||
Lisp_Object, Lisp_Object);
|
||||
static void compute_tip_xy (struct frame *, Lisp_Object, Lisp_Object,
|
||||
Lisp_Object, int, int, int *, int *);
|
||||
|
||||
|
@ -5348,9 +5346,7 @@ unwind_create_tip_frame (Lisp_Object frame)
|
|||
when this happens. */
|
||||
|
||||
static Lisp_Object
|
||||
x_create_tip_frame (struct x_display_info *dpyinfo,
|
||||
Lisp_Object parms,
|
||||
Lisp_Object text)
|
||||
x_create_tip_frame (struct x_display_info *dpyinfo, Lisp_Object parms)
|
||||
{
|
||||
struct frame *f;
|
||||
Lisp_Object frame;
|
||||
|
@ -5359,7 +5355,6 @@ x_create_tip_frame (struct x_display_info *dpyinfo,
|
|||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
bool face_change_before = face_change;
|
||||
Lisp_Object buffer;
|
||||
struct buffer *old_buffer;
|
||||
int x_width = 0, x_height = 0;
|
||||
|
||||
if (!dpyinfo->terminal->name)
|
||||
|
@ -5375,23 +5370,9 @@ x_create_tip_frame (struct x_display_info *dpyinfo,
|
|||
error ("Invalid frame name--not a string or nil");
|
||||
|
||||
frame = Qnil;
|
||||
f = make_frame (true);
|
||||
f = make_frame (false);
|
||||
f->wants_modeline = false;
|
||||
XSETFRAME (frame, f);
|
||||
|
||||
AUTO_STRING (tip, " *tip*");
|
||||
buffer = Fget_buffer_create (tip);
|
||||
/* Use set_window_buffer instead of Fset_window_buffer (see
|
||||
discussion of bug#11984, bug#12025, bug#12026). */
|
||||
set_window_buffer (FRAME_ROOT_WINDOW (f), buffer, false, false);
|
||||
old_buffer = current_buffer;
|
||||
set_buffer_internal_1 (XBUFFER (buffer));
|
||||
bset_truncate_lines (current_buffer, Qnil);
|
||||
specbind (Qinhibit_read_only, Qt);
|
||||
specbind (Qinhibit_modification_hooks, Qt);
|
||||
Ferase_buffer ();
|
||||
Finsert (1, &text);
|
||||
set_buffer_internal_1 (old_buffer);
|
||||
|
||||
record_unwind_protect (unwind_create_tip_frame, frame);
|
||||
|
||||
f->terminal = dpyinfo->terminal;
|
||||
|
@ -5633,8 +5614,6 @@ x_create_tip_frame (struct x_display_info *dpyinfo,
|
|||
{
|
||||
Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
|
||||
|
||||
/* Set tip_frame here, so that */
|
||||
tip_frame = frame;
|
||||
call2 (Qface_set_after_frame_default, frame, Qnil);
|
||||
|
||||
if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
|
||||
|
@ -5773,6 +5752,85 @@ compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx, Lisp_Object
|
|||
}
|
||||
|
||||
|
||||
/* Hide tooltip. Delete its frame if DELETE is true. */
|
||||
static Lisp_Object
|
||||
x_hide_tip (bool delete)
|
||||
{
|
||||
if (!NILP (tip_timer))
|
||||
{
|
||||
call1 (Qcancel_timer, tip_timer);
|
||||
tip_timer = Qnil;
|
||||
}
|
||||
|
||||
|
||||
if (NILP (tip_frame)
|
||||
|| (!delete && FRAMEP (tip_frame)
|
||||
&& !FRAME_VISIBLE_P (XFRAME (tip_frame))))
|
||||
return Qnil;
|
||||
else
|
||||
{
|
||||
ptrdiff_t count;
|
||||
Lisp_Object was_open = Qnil;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
#ifdef USE_GTK
|
||||
{
|
||||
/* When using system tooltip, tip_frame is the Emacs frame on
|
||||
which the tip is shown. */
|
||||
struct frame *f = XFRAME (tip_frame);
|
||||
|
||||
if (FRAME_LIVE_P (f) && xg_hide_tooltip (f))
|
||||
{
|
||||
tip_frame = Qnil;
|
||||
was_open = Qt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FRAMEP (tip_frame))
|
||||
{
|
||||
if (delete)
|
||||
{
|
||||
delete_frame (tip_frame, Qnil);
|
||||
tip_frame = Qnil;
|
||||
}
|
||||
else
|
||||
x_make_frame_invisible (XFRAME (tip_frame));
|
||||
|
||||
was_open = Qt;
|
||||
|
||||
#ifdef USE_LUCID
|
||||
/* Bloodcurdling hack alert: The Lucid menu bar widget's
|
||||
redisplay procedure is not called when a tip frame over
|
||||
menu items is unmapped. Redisplay the menu manually... */
|
||||
{
|
||||
Widget w;
|
||||
struct frame *f = SELECTED_FRAME ();
|
||||
if (FRAME_X_P (f) && FRAME_LIVE_P (f))
|
||||
{
|
||||
w = f->output_data.x->menubar_widget;
|
||||
|
||||
if (!DoesSaveUnders (FRAME_DISPLAY_INFO (f)->screen)
|
||||
&& w != NULL)
|
||||
{
|
||||
block_input ();
|
||||
xlwmenu_redisplay (w);
|
||||
unblock_input ();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* USE_LUCID */
|
||||
}
|
||||
else
|
||||
tip_frame = Qnil;
|
||||
|
||||
return unbind_to (count, was_open);
|
||||
}
|
||||
}
|
||||
|
||||
DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
|
||||
doc: /* Show STRING in a "tooltip" window on frame FRAME.
|
||||
A tooltip window is a small X window displaying a string.
|
||||
|
@ -5805,15 +5863,16 @@ A tooltip's maximum size is specified by `x-max-tooltip-size'.
|
|||
Text larger than the specified size is clipped. */)
|
||||
(Lisp_Object string, Lisp_Object frame, Lisp_Object parms, Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
|
||||
{
|
||||
struct frame *f;
|
||||
struct frame *f, *tip_f;
|
||||
struct window *w;
|
||||
int root_x, root_y;
|
||||
struct buffer *old_buffer;
|
||||
struct text_pos pos;
|
||||
int i, width, height;
|
||||
bool seen_reversed_p;
|
||||
int width, height;
|
||||
int old_windows_or_buffers_changed = windows_or_buffers_changed;
|
||||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
ptrdiff_t count_1;
|
||||
Lisp_Object window, size;
|
||||
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
|
||||
|
@ -5862,22 +5921,23 @@ Text larger than the specified size is clipped. */)
|
|||
if (NILP (last_show_tip_args))
|
||||
last_show_tip_args = Fmake_vector (make_number (3), Qnil);
|
||||
|
||||
if (!NILP (tip_frame))
|
||||
if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
|
||||
{
|
||||
Lisp_Object last_string = AREF (last_show_tip_args, 0);
|
||||
Lisp_Object last_frame = AREF (last_show_tip_args, 1);
|
||||
Lisp_Object last_parms = AREF (last_show_tip_args, 2);
|
||||
|
||||
if (EQ (frame, last_frame)
|
||||
&& !NILP (Fequal (last_string, string))
|
||||
if (FRAME_VISIBLE_P (XFRAME (tip_frame))
|
||||
&& EQ (frame, last_frame)
|
||||
&& !NILP (Fequal_including_properties (last_string, string))
|
||||
&& !NILP (Fequal (last_parms, parms)))
|
||||
{
|
||||
struct frame *tip_f = XFRAME (tip_frame);
|
||||
|
||||
/* Only DX and DY have changed. */
|
||||
tip_f = XFRAME (tip_frame);
|
||||
if (!NILP (tip_timer))
|
||||
{
|
||||
Lisp_Object timer = tip_timer;
|
||||
|
||||
tip_timer = Qnil;
|
||||
call1 (Qcancel_timer, timer);
|
||||
}
|
||||
|
@ -5888,41 +5948,103 @@ Text larger than the specified size is clipped. */)
|
|||
XMoveWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f),
|
||||
root_x, root_y);
|
||||
unblock_input ();
|
||||
|
||||
goto start_timer;
|
||||
}
|
||||
}
|
||||
else if (tooltip_reuse_hidden_frame && EQ (frame, last_frame))
|
||||
{
|
||||
bool delete = false;
|
||||
Lisp_Object tail, elt, parm, last;
|
||||
|
||||
/* Hide a previous tip, if any. */
|
||||
Fx_hide_tip ();
|
||||
/* Check if every parameter in PARMS has the same value in
|
||||
last_parms unless it should be ignored by means of
|
||||
Vtooltip_reuse_hidden_frame_parameters. This may destruct
|
||||
last_parms which, however, will be recreated below. */
|
||||
for (tail = parms; CONSP (tail); tail = XCDR (tail))
|
||||
{
|
||||
elt = XCAR (tail);
|
||||
parm = Fcar (elt);
|
||||
/* The left, top, right and bottom parameters are handled
|
||||
by compute_tip_xy so they can be ignored here. */
|
||||
if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
|
||||
&& !EQ (parm, Qright) && !EQ (parm, Qbottom))
|
||||
{
|
||||
last = Fassq (parm, last_parms);
|
||||
if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
|
||||
{
|
||||
/* We lost, delete the old tooltip. */
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
last_parms = call2 (Qassq_delete_all, parm, last_parms);
|
||||
}
|
||||
else
|
||||
last_parms = call2 (Qassq_delete_all, parm, last_parms);
|
||||
}
|
||||
|
||||
/* Now check if every parameter in what is left of last_parms
|
||||
with a non-nil value has an association in PARMS unless it
|
||||
should be ignored by means of
|
||||
Vtooltip_reuse_hidden_frame_parameters. */
|
||||
for (tail = last_parms; CONSP (tail); tail = XCDR (tail))
|
||||
{
|
||||
elt = XCAR (tail);
|
||||
parm = Fcar (elt);
|
||||
if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
|
||||
&& !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
|
||||
{
|
||||
/* We lost, delete the old tooltip. */
|
||||
delete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
x_hide_tip (delete);
|
||||
}
|
||||
else
|
||||
x_hide_tip (true);
|
||||
}
|
||||
else
|
||||
x_hide_tip (true);
|
||||
|
||||
ASET (last_show_tip_args, 0, string);
|
||||
ASET (last_show_tip_args, 1, frame);
|
||||
ASET (last_show_tip_args, 2, parms);
|
||||
|
||||
/* Add default values to frame parameters. */
|
||||
if (NILP (Fassq (Qname, parms)))
|
||||
parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
|
||||
if (NILP (Fassq (Qinternal_border_width, parms)))
|
||||
parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms);
|
||||
if (NILP (Fassq (Qborder_width, parms)))
|
||||
parms = Fcons (Fcons (Qborder_width, make_number (1)), parms);
|
||||
if (NILP (Fassq (Qbottom_divider_width, parms)))
|
||||
parms = Fcons (Fcons (Qbottom_divider_width, make_number (0)), parms);
|
||||
if (NILP (Fassq (Qright_divider_width, parms)))
|
||||
parms = Fcons (Fcons (Qright_divider_width, make_number (0)), parms);
|
||||
if (NILP (Fassq (Qborder_color, parms)))
|
||||
parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms);
|
||||
if (NILP (Fassq (Qbackground_color, parms)))
|
||||
parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
|
||||
parms);
|
||||
if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
|
||||
{
|
||||
/* Add default values to frame parameters. */
|
||||
if (NILP (Fassq (Qname, parms)))
|
||||
parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
|
||||
if (NILP (Fassq (Qinternal_border_width, parms)))
|
||||
parms = Fcons (Fcons (Qinternal_border_width, make_number (3)), parms);
|
||||
if (NILP (Fassq (Qborder_width, parms)))
|
||||
parms = Fcons (Fcons (Qborder_width, make_number (1)), parms);
|
||||
if (NILP (Fassq (Qborder_color, parms)))
|
||||
parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")), parms);
|
||||
if (NILP (Fassq (Qbackground_color, parms)))
|
||||
parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
|
||||
parms);
|
||||
|
||||
/* Create a frame for the tooltip, and record it in the global
|
||||
variable tip_frame. */
|
||||
frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms, string);
|
||||
f = XFRAME (frame);
|
||||
/* Create a frame for the tooltip, and record it in the global
|
||||
variable tip_frame. */
|
||||
if (NILP (tip_frame = x_create_tip_frame (FRAME_DISPLAY_INFO (f), parms)))
|
||||
/* Creating the tip frame failed. */
|
||||
return unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Set up the frame's root window. */
|
||||
w = XWINDOW (FRAME_ROOT_WINDOW (f));
|
||||
tip_f = XFRAME (tip_frame);
|
||||
window = FRAME_ROOT_WINDOW (tip_f);
|
||||
AUTO_STRING (tip, " *tip*");
|
||||
set_window_buffer (window, Fget_buffer_create (tip), false, false);
|
||||
w = XWINDOW (window);
|
||||
w->pseudo_window_p = true;
|
||||
|
||||
/* Set up the frame's root window. Note: The following code does not
|
||||
try to size the window or its frame correctly. Its only purpose is
|
||||
to make the subsequent text size calculations work. The right
|
||||
sizes should get installed when the toolkit gets back to us. */
|
||||
w->left_col = 0;
|
||||
w->top_line = 0;
|
||||
w->pixel_left = 0;
|
||||
|
@ -5941,130 +6063,47 @@ Text larger than the specified size is clipped. */)
|
|||
w->total_lines = 40;
|
||||
}
|
||||
|
||||
w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (f);
|
||||
w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (f);
|
||||
w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
|
||||
w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
|
||||
FRAME_TOTAL_COLS (tip_f) = w->total_cols;
|
||||
adjust_frame_glyphs (tip_f);
|
||||
|
||||
FRAME_TOTAL_COLS (f) = w->total_cols;
|
||||
adjust_frame_glyphs (f);
|
||||
w->pseudo_window_p = true;
|
||||
|
||||
/* Display the tooltip text in a temporary buffer. */
|
||||
/* Insert STRING into root window's buffer and fit the frame to the
|
||||
buffer. */
|
||||
count_1 = SPECPDL_INDEX ();
|
||||
old_buffer = current_buffer;
|
||||
set_buffer_internal_1 (XBUFFER (XWINDOW (FRAME_ROOT_WINDOW (f))->contents));
|
||||
set_buffer_internal_1 (XBUFFER (w->contents));
|
||||
bset_truncate_lines (current_buffer, Qnil);
|
||||
specbind (Qinhibit_read_only, Qt);
|
||||
specbind (Qinhibit_modification_hooks, Qt);
|
||||
specbind (Qinhibit_point_motion_hooks, Qt);
|
||||
Ferase_buffer ();
|
||||
Finsert (1, &string);
|
||||
clear_glyph_matrix (w->desired_matrix);
|
||||
clear_glyph_matrix (w->current_matrix);
|
||||
SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
|
||||
try_window (FRAME_ROOT_WINDOW (f), pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
|
||||
try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
|
||||
/* Calculate size of tooltip window. */
|
||||
size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
|
||||
make_number (w->pixel_height), Qnil);
|
||||
/* Add the frame's internal border to calculated size. */
|
||||
width = XINT (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
|
||||
height = XINT (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
|
||||
|
||||
/* Compute width and height of the tooltip. */
|
||||
width = height = 0;
|
||||
seen_reversed_p = false;
|
||||
for (i = 0; i < w->desired_matrix->nrows; ++i)
|
||||
{
|
||||
struct glyph_row *row = &w->desired_matrix->rows[i];
|
||||
struct glyph *last;
|
||||
int row_width;
|
||||
|
||||
/* Stop at the first empty row at the end. */
|
||||
if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row))
|
||||
break;
|
||||
|
||||
/* Let the row go over the full width of the frame. */
|
||||
row->full_width_p = true;
|
||||
|
||||
row_width = row->pixel_width;
|
||||
if (row->used[TEXT_AREA])
|
||||
{
|
||||
/* There's a glyph at the end of rows that is used to place
|
||||
the cursor there. Don't include the width of this glyph. */
|
||||
if (!row->reversed_p)
|
||||
{
|
||||
last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1];
|
||||
if (NILP (last->object))
|
||||
row_width -= last->pixel_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There could be a stretch glyph at the beginning of R2L
|
||||
rows that is produced by extend_face_to_end_of_line.
|
||||
Don't count that glyph. */
|
||||
struct glyph *g = row->glyphs[TEXT_AREA];
|
||||
|
||||
if (g->type == STRETCH_GLYPH && NILP (g->object))
|
||||
{
|
||||
row_width -= g->pixel_width;
|
||||
seen_reversed_p = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
height += row->height;
|
||||
width = max (width, row_width);
|
||||
}
|
||||
|
||||
/* If we've seen partial-length R2L rows, we need to re-adjust the
|
||||
tool-tip frame width and redisplay it again, to avoid over-wide
|
||||
tips due to the stretch glyph that extends R2L lines to full
|
||||
width of the frame. */
|
||||
if (seen_reversed_p)
|
||||
{
|
||||
/* w->total_cols and FRAME_TOTAL_COLS want the width in columns,
|
||||
not in pixels. */
|
||||
w->pixel_width = width;
|
||||
width /= WINDOW_FRAME_COLUMN_WIDTH (w);
|
||||
w->total_cols = width;
|
||||
FRAME_TOTAL_COLS (f) = width;
|
||||
SET_FRAME_WIDTH (f, width);
|
||||
adjust_frame_glyphs (f);
|
||||
clear_glyph_matrix (w->desired_matrix);
|
||||
clear_glyph_matrix (w->current_matrix);
|
||||
try_window (FRAME_ROOT_WINDOW (f), pos, 0);
|
||||
width = height = 0;
|
||||
/* Recompute width and height of the tooltip. */
|
||||
for (i = 0; i < w->desired_matrix->nrows; ++i)
|
||||
{
|
||||
struct glyph_row *row = &w->desired_matrix->rows[i];
|
||||
struct glyph *last;
|
||||
int row_width;
|
||||
|
||||
if (!row->enabled_p || !MATRIX_ROW_DISPLAYS_TEXT_P (row))
|
||||
break;
|
||||
row->full_width_p = true;
|
||||
row_width = row->pixel_width;
|
||||
if (row->used[TEXT_AREA] && !row->reversed_p)
|
||||
{
|
||||
last = &row->glyphs[TEXT_AREA][row->used[TEXT_AREA] - 1];
|
||||
if (NILP (last->object))
|
||||
row_width -= last->pixel_width;
|
||||
}
|
||||
|
||||
height += row->height;
|
||||
width = max (width, row_width);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the frame's internal border to the width and height the X
|
||||
window should have. */
|
||||
height += 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
|
||||
width += 2 * FRAME_INTERNAL_BORDER_WIDTH (f);
|
||||
|
||||
/* Move the tooltip window where the mouse pointer is. Resize and
|
||||
show it. */
|
||||
compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y);
|
||||
/* Calculate position of tooltip frame. */
|
||||
compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
|
||||
|
||||
/* Show tooltip frame. */
|
||||
block_input ();
|
||||
XMoveResizeWindow (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f),
|
||||
XMoveResizeWindow (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f),
|
||||
root_x, root_y, width, height);
|
||||
XMapRaised (FRAME_X_DISPLAY (f), FRAME_X_WINDOW (f));
|
||||
XMapRaised (FRAME_X_DISPLAY (tip_f), FRAME_X_WINDOW (tip_f));
|
||||
unblock_input ();
|
||||
|
||||
/* Draw into the window. */
|
||||
w->must_be_updated_p = true;
|
||||
update_single_window (w);
|
||||
|
||||
/* Restore original current buffer. */
|
||||
set_buffer_internal_1 (old_buffer);
|
||||
unbind_to (count_1, Qnil);
|
||||
windows_or_buffers_changed = old_windows_or_buffers_changed;
|
||||
|
||||
start_timer:
|
||||
|
@ -6081,66 +6120,9 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
|
|||
Value is t if tooltip was open, nil otherwise. */)
|
||||
(void)
|
||||
{
|
||||
ptrdiff_t count;
|
||||
Lisp_Object deleted, frame, timer;
|
||||
|
||||
/* Return quickly if nothing to do. */
|
||||
if (NILP (tip_timer) && NILP (tip_frame))
|
||||
return Qnil;
|
||||
|
||||
frame = tip_frame;
|
||||
timer = tip_timer;
|
||||
tip_frame = tip_timer = deleted = Qnil;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
specbind (Qinhibit_redisplay, Qt);
|
||||
specbind (Qinhibit_quit, Qt);
|
||||
|
||||
if (!NILP (timer))
|
||||
call1 (Qcancel_timer, timer);
|
||||
|
||||
#ifdef USE_GTK
|
||||
{
|
||||
/* When using system tooltip, tip_frame is the Emacs frame on which
|
||||
the tip is shown. */
|
||||
struct frame *f = XFRAME (frame);
|
||||
if (FRAME_LIVE_P (f) && xg_hide_tooltip (f))
|
||||
frame = Qnil;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (FRAMEP (frame))
|
||||
{
|
||||
delete_frame (frame, Qnil);
|
||||
deleted = Qt;
|
||||
|
||||
#ifdef USE_LUCID
|
||||
/* Bloodcurdling hack alert: The Lucid menu bar widget's
|
||||
redisplay procedure is not called when a tip frame over menu
|
||||
items is unmapped. Redisplay the menu manually... */
|
||||
{
|
||||
Widget w;
|
||||
struct frame *f = SELECTED_FRAME ();
|
||||
if (FRAME_X_P (f) && FRAME_LIVE_P (f))
|
||||
{
|
||||
w = f->output_data.x->menubar_widget;
|
||||
|
||||
if (!DoesSaveUnders (FRAME_DISPLAY_INFO (f)->screen)
|
||||
&& w != NULL)
|
||||
{
|
||||
block_input ();
|
||||
xlwmenu_redisplay (w);
|
||||
unblock_input ();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* USE_LUCID */
|
||||
}
|
||||
|
||||
return unbind_to (count, deleted);
|
||||
return x_hide_tip (!tooltip_reuse_hidden_frame);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
File selection dialog
|
||||
|
@ -6802,6 +6784,7 @@ syms_of_xfns (void)
|
|||
DEFSYM (Qcancel_timer, "cancel-timer");
|
||||
DEFSYM (Qfont_param, "font-parameter");
|
||||
DEFSYM (Qmono, "mono");
|
||||
DEFSYM (Qassq_delete_all, "assq-delete-all");
|
||||
|
||||
#ifdef USE_CAIRO
|
||||
DEFSYM (Qpdf, "pdf");
|
||||
|
|
Loading…
Add table
Reference in a new issue