emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 09e86785ce: Implement non-system tooltips on NS


From: Po Lu
Subject: master 09e86785ce: Implement non-system tooltips on NS
Date: Fri, 13 May 2022 23:30:42 -0400 (EDT)

branch: master
commit 09e86785ce2dae9176f4122c399c61b51240cfec
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Implement non-system tooltips on NS
    
    * src/nsfns.m (unwind_create_frame): Return Lisp_Object like on
    X.
    (do_unwind_create_frame): New function.
    (Fx_create_frame): Adjust accordingly.
    (compute_tip_xy): Fix coding style.
    (unwind_create_tip_frame, ns_create_tip_frame): New functions.
    (x_hide_tip, Fx_show_tip, Fx_hide_tip): Create and hide actual
    tooltip frames.
    (syms_of_nsfns): New defvar `x-max-tooltip-size' and staticpros.
    
    * src/nsterm.m (ns_set_window_size): Clean up coding style.
    ([EmacsWindow initWithEmacsFrame:]):
    ([EmacsWindow initWithEmacsFrame:fullscreen:screen:]): Handle
    tooltip frames.
---
 src/nsfns.m  | 635 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/nsterm.h |   1 +
 src/nsterm.m |  45 ++---
 3 files changed, 619 insertions(+), 62 deletions(-)

diff --git a/src/nsfns.m b/src/nsfns.m
index a67dafe095..f82665a300 100644
--- a/src/nsfns.m
+++ b/src/nsfns.m
@@ -53,6 +53,26 @@ GNUstep port and post-20 update by Adrian Robert 
(arobert@cogsci.ucsd.edu)
 
 static EmacsTooltip *ns_tooltip = nil;
 
+/* The frame of the currently visible tooltip, or nil if none.  */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+   currently visible tooltip.  */
+static NSWindow *tip_window;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+   fires.  */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_parms;
+
 /* Static variables to handle AppleScript execution.  */
 static Lisp_Object as_script, *as_result;
 static int as_status;
@@ -1021,7 +1041,7 @@ frame_parm_handler ns_frame_parm_handlers[] =
 /* Handler for signals raised during x_create_frame.
    FRAME is the frame which is partially constructed.  */
 
-static void
+static Lisp_Object
 unwind_create_frame (Lisp_Object frame)
 {
   struct frame *f = XFRAME (frame);
@@ -1030,7 +1050,7 @@ unwind_create_frame (Lisp_Object frame)
      display is disconnected after the frame has become official, but
      before x_create_frame removes the unwind protect.  */
   if (!FRAME_LIVE_P (f))
-    return;
+    return Qnil;
 
   /* If frame is ``official'', nothing to do.  */
   if (NILP (Fmemq (frame, Vframe_list)))
@@ -1057,7 +1077,18 @@ unwind_create_frame (Lisp_Object frame)
       /* Check that reference counts are indeed correct.  */
       eassert (dpyinfo->terminal->image_cache->refcount == 
image_cache_refcount);
 #endif
+
+      return Qt;
     }
+
+  return Qnil;
+}
+
+
+static void
+do_unwind_create_frame (Lisp_Object frame)
+{
+  unwind_create_frame (frame);
 }
 
 /*
@@ -1191,7 +1222,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
   FRAME_DISPLAY_INFO (f) = dpyinfo;
 
   /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe.  */
-  record_unwind_protect (unwind_create_frame, frame);
+  record_unwind_protect (do_unwind_create_frame, frame);
 
   f->output_data.ns->window_desc = desc_ctr++;
   if (TYPE_RANGED_FIXNUMP (Window, parent))
@@ -2769,16 +2800,10 @@ DEFUN ("x-display-color-cells", Fx_display_color_cells, 
Sx_display_color_cells,
   return make_fixnum (1 << min (dpyinfo->n_planes, 24));
 }
 
-/* TODO: move to xdisp or similar */
 static void
-compute_tip_xy (struct frame *f,
-                Lisp_Object parms,
-                Lisp_Object dx,
-                Lisp_Object dy,
-                int width,
-                int height,
-                int *root_x,
-                int *root_y)
+compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx,
+               Lisp_Object dy, int width, int height, int *root_x,
+               int *root_y)
 {
   Lisp_Object left, top, right, bottom;
   NSPoint pt;
@@ -2847,6 +2872,299 @@ compute_tip_xy (struct frame *f,
     *root_y = screen.frame.origin.y + screen.frame.size.height - height;
 }
 
+static void
+unwind_create_tip_frame (Lisp_Object frame)
+{
+  Lisp_Object deleted;
+
+  deleted = unwind_create_frame (frame);
+  if (EQ (deleted, Qt))
+    {
+      tip_window = NULL;
+      tip_frame = Qnil;
+    }
+}
+
+/* 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.
+
+   Note that functions called here, esp. gui_default_parameter can
+   signal errors, for instance when a specified color name is
+   undefined.  We have to make sure that we're in a consistent state
+   when this happens.  */
+
+static Lisp_Object
+ns_create_tip_frame (struct ns_display_info *dpyinfo, Lisp_Object parms)
+{
+  struct frame *f;
+  Lisp_Object frame;
+  Lisp_Object name;
+  specpdl_ref count = SPECPDL_INDEX ();
+  bool face_change_before = face_change;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  parms = Fcopy_alist (parms);
+
+  /* Get the name of the frame to use for resource lookup.  */
+  name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+                              RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && !EQ (name, Qunbound)
+      && !NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  frame = Qnil;
+  f = make_frame (false);
+  f->wants_modeline = false;
+  XSETFRAME (frame, f);
+  record_unwind_protect (unwind_create_tip_frame, frame);
+
+  f->terminal = dpyinfo->terminal;
+
+  f->output_method = output_ns;
+  f->output_data.ns = xzalloc (sizeof *f->output_data.ns);
+  f->tooltip = true;
+
+  FRAME_FONTSET (f) = -1;
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+  block_input ();
+#ifdef NS_IMPL_COCOA
+  mac_register_font_driver (f);
+#else
+  register_font_driver (&nsfont_driver, f);
+#endif
+  unblock_input ();
+
+  image_cache_refcount =
+    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+
+  gui_default_parameter (f, parms, Qfont_backend, Qnil,
+                         "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+  {
+#ifdef NS_IMPL_COCOA
+    /* use for default font name */
+    id font = [NSFont userFixedPitchFontOfSize: -1.0]; /* default */
+    gui_default_parameter (f, parms, Qfontsize,
+                           make_fixnum (0 /* (int)[font pointSize] */),
+                           "fontSize", "FontSize", RES_TYPE_NUMBER);
+    // Remove ' Regular', not handled by backends.
+    char *fontname = xstrdup ([[font displayName] UTF8String]);
+    int len = strlen (fontname);
+    if (len > 8 && strcmp (fontname + len - 8, " Regular") == 0)
+      fontname[len-8] = '\0';
+    gui_default_parameter (f, parms, Qfont,
+                           build_string (fontname),
+                           "font", "Font", RES_TYPE_STRING);
+    xfree (fontname);
+#else
+    gui_default_parameter (f, parms, Qfont,
+                           build_string ("fixed"),
+                           "font", "Font", RES_TYPE_STRING);
+#endif
+  }
+
+  gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+                         "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+  /* This defaults to 1 in order to match xterm.  We recognize either
+     internalBorderWidth or internalBorder (which is what xterm calls
+     it).  */
+  if (NILP (Fassq (Qinternal_border_width, parms)))
+    {
+      Lisp_Object value;
+
+      value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+                                   "internalBorder", "internalBorder",
+                                   RES_TYPE_NUMBER);
+      if (! EQ (value, Qunbound))
+       parms = Fcons (Fcons (Qinternal_border_width, value),
+                      parms);
+    }
+
+  gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
+                         "internalBorderWidth", "internalBorderWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+                         NULL, NULL, RES_TYPE_NUMBER);
+
+  /* Also do the stuff which must be set before the window exists.  */
+  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+                         "foreground", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+                         "background", "Background", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+                         "pointerColor", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
+                         "cursorColor", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+                         "borderColor", "BorderColor", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+
+  /* Init faces before gui_default_parameter is called for the
+     scroll-bar-width parameter because otherwise we end up in
+     init_iterator with a null face cache, which should not happen.  */
+  init_frame_faces (f);
+
+  f->output_data.ns->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                         "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                         RES_TYPE_BOOLEAN);
+
+  gui_figure_window_size (f, parms, false, false);
+
+  block_input ();
+  [[EmacsView alloc] initFrameFromEmacs: f];
+  ns_icon (f, parms);
+  unblock_input ();
+
+  gui_default_parameter (f, parms, Qauto_raise, Qnil,
+                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qauto_lower, Qnil,
+                         "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qcursor_type, Qbox,
+                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qalpha, Qnil,
+                         "alpha", "Alpha", RES_TYPE_NUMBER);
+
+  /* Add `tooltip' frame parameter's default value. */
+  if (NILP (Fframe_parameter (frame, Qtooltip)))
+    {
+      AUTO_FRAME_ARG (arg, Qtooltip, Qt);
+      Fmodify_frame_parameters (frame, arg);
+    }
+
+  /* FIXME - can this be done in a similar way to normal frames?
+     https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */
+
+  /* Set the `display-type' frame parameter before setting up faces. */
+  {
+    Lisp_Object disptype = intern ("color");
+
+    if (NILP (Fframe_parameter (frame, Qdisplay_type)))
+      {
+       AUTO_FRAME_ARG (arg, Qdisplay_type, disptype);
+       Fmodify_frame_parameters (frame, arg);
+      }
+  }
+
+  /* Set up faces after all frame parameters are known.  This call
+     also merges in face attributes specified for new frames.
+
+     Frame parameters may be changed if .Xdefaults contains
+     specifications for the default font.  For example, if there is an
+     `Emacs.default.attributeBackground: pink', the `background-color'
+     attribute of the frame gets set, which let's the internal border
+     of the tooltip frame appear in pink.  Prevent this.  */
+  {
+    Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
+
+    call2 (Qface_set_after_frame_default, frame, Qnil);
+
+    if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
+      {
+       AUTO_FRAME_ARG (arg, Qbackground_color, bg);
+       Fmodify_frame_parameters (frame, arg);
+      }
+  }
+
+  f->no_split = true;
+
+  /* Now that the frame will be official, it counts as a reference to
+     its display and terminal.  */
+  f->terminal->reference_count++;
+
+  /* It is now ok to make the frame official even if we get an error
+     below.  And the frame needs to be on Vframe_list or making it
+     visible won't work.  */
+  Vframe_list = Fcons (frame, Vframe_list);
+  f->can_set_window_size = true;
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+                    0, true, Qtip_frame);
+
+  /* Setting attributes of faces of the tooltip frame from resources
+     and similar will set face_change, which leads to the clearing of
+     all current matrices.  Since this isn't necessary here, avoid it
+     by resetting face_change to the value it had before we created
+     the tip frame.  */
+  face_change = face_change_before;
+
+  /* Discard the unwind_protect.  */
+  return unbind_to (count, frame);
+}
+
+static Lisp_Object
+x_hide_tip (bool delete)
+{
+  if (!NILP (tip_timer))
+    {
+      call1 (intern ("cancel-timer"), tip_timer);
+      tip_timer = Qnil;
+    }
+
+  if (!(ns_tooltip == nil || ![ns_tooltip isActive]))
+    {
+      [ns_tooltip hide];
+      tip_last_frame = Qnil;
+      return Qt;
+    }
+
+  if ((NILP (tip_last_frame) && NILP (tip_frame))
+      || (!use_system_tooltips
+         && !delete
+         && !NILP (tip_frame)
+         && FRAME_LIVE_P (XFRAME (tip_frame))
+         && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+    /* Either there's no tooltip to hide or it's an already invisible
+       Emacs tooltip and we don't want to change its type.  Return
+       quickly.  */
+    return Qnil;
+  else
+    {
+      specpdl_ref count;
+      Lisp_Object was_open = Qnil;
+
+      count = SPECPDL_INDEX ();
+      specbind (Qinhibit_redisplay, Qt);
+      specbind (Qinhibit_quit, Qt);
+
+      /* Now look whether there's an Emacs tip around.  */
+      if (!NILP (tip_frame))
+       {
+         struct frame *f = XFRAME (tip_frame);
+
+         if (FRAME_LIVE_P (f))
+           {
+             if (delete || use_system_tooltips)
+               {
+                 /* Delete the Emacs tooltip frame when DELETE is true
+                    or we change the tooltip type from an Emacs one to
+                    a GTK+ system one.  */
+                 delete_frame (tip_frame, Qnil);
+                 tip_frame = Qnil;
+               }
+             else
+               ns_make_frame_invisible (f);
+
+             was_open = Qt;
+           }
+         else
+           tip_frame = Qnil;
+       }
+      else
+       tip_frame = Qnil;
+
+      return unbind_to (count, was_open);
+    }
+}
 
 DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
        doc: /* SKIP: real doc in xfns.c.  */)
@@ -2854,11 +3172,18 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
 {
   int root_x, root_y;
   specpdl_ref count = SPECPDL_INDEX ();
-  struct frame *f;
+  struct frame *f, *tip_f;
+  struct window *w;
+  struct buffer *old_buffer;
+  struct text_pos pos;
+  int width, height;
+  int old_windows_or_buffers_changed = windows_or_buffers_changed;
+  specpdl_ref count_1;
+  Lisp_Object window, size, tip_buf;
   char *str;
-  NSSize size;
-  NSColor *color;
-  Lisp_Object t;
+  NSWindow *nswindow;
+
+  AUTO_STRING (tip, " *tip*");
 
   specbind (Qinhibit_redisplay, Qt);
 
@@ -2879,32 +3204,250 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
   else
     CHECK_FIXNUM (dy);
 
-  block_input ();
-  if (ns_tooltip == nil)
-    ns_tooltip = [[EmacsTooltip alloc] init];
+  if (use_system_tooltips)
+    {
+      NSSize size;
+      NSColor *color;
+      Lisp_Object t;
+
+      block_input ();
+      if (ns_tooltip == nil)
+       ns_tooltip = [[EmacsTooltip alloc] init];
+      else
+       Fx_hide_tip ();
+
+      t = gui_display_get_arg (NULL, parms, Qbackground_color, NULL, NULL,
+                              RES_TYPE_STRING);
+      if (ns_lisp_to_color (t, &color) == 0)
+       [ns_tooltip setBackgroundColor: color];
+
+      t = gui_display_get_arg (NULL, parms, Qforeground_color, NULL, NULL,
+                              RES_TYPE_STRING);
+      if (ns_lisp_to_color (t, &color) == 0)
+       [ns_tooltip setForegroundColor: color];
+
+      [ns_tooltip setText: str];
+      size = [ns_tooltip frame].size;
+
+      /* Move the tooltip window where the mouse pointer is.  Resize and
+        show it.  */
+      compute_tip_xy (f, parms, dx, dy, (int) size.width, (int) size.height,
+                     &root_x, &root_y);
+
+      [ns_tooltip showAtX: root_x Y: root_y for: XFIXNUM (timeout)];
+      unblock_input ();
+    }
   else
-    Fx_hide_tip ();
+    {
+      if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+       {
+         if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+             && EQ (frame, tip_last_frame)
+             && !NILP (Fequal_including_properties (tip_last_string, string))
+             && !NILP (Fequal (tip_last_parms, parms)))
+           {
+             /* Only DX and DY have changed.  */
+             tip_f = XFRAME (tip_frame);
+             if (!NILP (tip_timer))
+               {
+                 call1 (intern ("cancel-timer"), tip_timer);
+                 tip_timer = Qnil;
+               }
+
+             nswindow = [FRAME_NS_VIEW (tip_f) window];
+
+             block_input ();
+             compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+                             FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+             [nswindow setFrame: NSMakeRect (root_x, root_y,
+                                             FRAME_PIXEL_WIDTH (tip_f),
+                                             FRAME_PIXEL_HEIGHT (tip_f))
+                        display: YES];
+             [nswindow setLevel: NSPopUpMenuWindowLevel];
+             [nswindow orderFront: NSApp];
+             [nswindow display];
+
+             SET_FRAME_VISIBLE (tip_f, 1);
+             unblock_input ();
+
+             goto start_timer;
+           }
+         else if (tooltip_reuse_hidden_frame && EQ (frame, tip_last_frame))
+           {
+             bool delete = false;
+             Lisp_Object tail, elt, parm, last;
+
+             /* Check if every parameter in PARMS has the same value in
+                tip_last_parms.  This may destruct tip_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, tip_last_parms);
+                     if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
+                       {
+                         /* We lost, delete the old tooltip.  */
+                         delete = true;
+                         break;
+                       }
+                     else
+                       tip_last_parms =
+                         call2 (intern ("assq-delete-all"), parm, 
tip_last_parms);
+                   }
+                 else
+                   tip_last_parms =
+                     call2 (intern ("assq-delete-all"), parm, tip_last_parms);
+               }
+
+             /* Now check if every parameter in what is left of
+                tip_last_parms with a non-nil value has an association in
+                PARMS.  */
+             for (tail = tip_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);
+
+      tip_last_frame = frame;
+      tip_last_string = string;
+      tip_last_parms = parms;
 
-  t = gui_display_get_arg (NULL, parms, Qbackground_color, NULL, NULL,
-                           RES_TYPE_STRING);
-  if (ns_lisp_to_color (t, &color) == 0)
-    [ns_tooltip setBackgroundColor: color];
+      if (NILP (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_fixnum (3)), 
parms);
+         if (NILP (Fassq (Qborder_width, parms)))
+           parms = Fcons (Fcons (Qborder_width, make_fixnum (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.  */
+         if (NILP (tip_frame = ns_create_tip_frame (FRAME_DISPLAY_INFO (f), 
parms)))
+           /* Creating the tip frame failed.  */
+           return unbind_to (count, Qnil);
+       }
 
-  t = gui_display_get_arg (NULL, parms, Qforeground_color, NULL, NULL,
-                           RES_TYPE_STRING);
-  if (ns_lisp_to_color (t, &color) == 0)
-    [ns_tooltip setForegroundColor: color];
+      tip_f = XFRAME (tip_frame);
+      window = FRAME_ROOT_WINDOW (tip_f);
+      tip_buf = Fget_buffer_create (tip, Qnil);
+      /* We will mark the tip window a "pseudo-window" below, and such
+        windows cannot have display margins.  */
+      bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+      bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+      set_window_buffer (window, tip_buf, false, false);
+      w = XWINDOW (window);
+      w->pseudo_window_p = true;
+      /* Try to avoid that `other-window' select us (Bug#47207).  */
+      Fset_window_parameter (window, Qno_other_window, Qt);
+
+      /* 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)
+         && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+         && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+       {
+         w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+         w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+       }
+      else
+       {
+         w->total_cols = 80;
+         w->total_lines = 40;
+       }
 
-  [ns_tooltip setText: str];
-  size = [ns_tooltip frame].size;
+      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);
+
+      /* 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 (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 (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+      /* Calculate size of tooltip window.  */
+      size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+                                     make_fixnum (w->pixel_height), Qnil,
+                                     Qnil);
+      /* Add the frame's internal border to calculated size.  */
+      width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+      height = XFIXNUM (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);
+
+      block_input ();
+      nswindow = [FRAME_NS_VIEW (tip_f) window];
+      [nswindow setFrame: NSMakeRect (root_x, root_y,
+                                     width, height)
+                display: YES];
+      [nswindow setLevel: NSPopUpMenuWindowLevel];
+      [nswindow orderFront: NSApp];
+      [nswindow display];
+
+      SET_FRAME_VISIBLE (tip_f, YES);
+      FRAME_PIXEL_WIDTH (tip_f) = width;
+      FRAME_PIXEL_HEIGHT (tip_f) = height;
+      unblock_input ();
 
-  /* Move the tooltip window where the mouse pointer is.  Resize and
-     show it.  */
-  compute_tip_xy (f, parms, dx, dy, (int)size.width, (int)size.height,
-                 &root_x, &root_y);
+      w->must_be_updated_p = true;
+      update_single_window (w);
+      flush_frame (tip_f);
+      set_buffer_internal_1 (old_buffer);
+      unbind_to (count_1, Qnil);
+      windows_or_buffers_changed = old_windows_or_buffers_changed;
 
-  [ns_tooltip showAtX: root_x Y: root_y for: XFIXNUM (timeout)];
-  unblock_input ();
+    start_timer:
+      /* Let the tip disappear after timeout seconds.  */
+      tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
+                        intern ("x-hide-tip"));
+    }
 
   return unbind_to (count, Qnil);
 }
@@ -2914,10 +3457,7 @@ DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
        doc: /* SKIP: real doc in xfns.c.  */)
      (void)
 {
-  if (ns_tooltip == nil || ![ns_tooltip isActive])
-    return Qnil;
-  [ns_tooltip hide];
-  return Qt;
+  return x_hide_tip (!tooltip_reuse_hidden_frame);
 }
 
 /* Return geometric attributes of FRAME.  According to the value of
@@ -3260,6 +3800,10 @@ be used as the image of the icon representing the frame. 
 */);
 Default is t.  */);
   ns_use_proxy_icon = true;
 
+  DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
+    doc: /* SKIP: real doc in xfns.c.  */);
+  Vx_max_tooltip_size = Fcons (make_fixnum (80), make_fixnum (40));
+
   defsubr (&Sns_read_file_name);
   defsubr (&Sns_get_resource);
   defsubr (&Sns_set_resource);
@@ -3309,6 +3853,17 @@ Default is t.  */);
   defsubr (&Sx_show_tip);
   defsubr (&Sx_hide_tip);
 
+  tip_timer = Qnil;
+  staticpro (&tip_timer);
+  tip_frame = Qnil;
+  staticpro (&tip_frame);
+  tip_last_frame = Qnil;
+  staticpro (&tip_last_frame);
+  tip_last_string = Qnil;
+  staticpro (&tip_last_string);
+  tip_last_parms = Qnil;
+  staticpro (&tip_last_parms);
+
 #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
   defsubr (&Ssystem_move_file_to_trash);
 #endif
diff --git a/src/nsterm.h b/src/nsterm.h
index ce2355e6b1..1135225e7b 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -1176,6 +1176,7 @@ extern size_t ns_image_size_in_bytes (void *img);
 /* This in nsterm.m */
 extern float ns_antialias_threshold;
 extern void ns_make_frame_visible (struct frame *f);
+extern void ns_make_frame_invisible (struct frame *f);
 extern void ns_iconify_frame (struct frame *f);
 extern void ns_set_undecorated (struct frame *f, Lisp_Object new_value,
                                 Lisp_Object old_value);
diff --git a/src/nsterm.m b/src/nsterm.m
index 9dff33da50..a1cbbff1cc 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1517,7 +1517,7 @@ ns_make_frame_visible (struct frame *f)
 }
 
 
-static void
+void
 ns_make_frame_invisible (struct frame *f)
 /* --------------------------------------------------------------------------
      Hide the window (X11 semantics)
@@ -1708,10 +1708,8 @@ ns_set_offset (struct frame *f, int xoff, int yoff, int 
change_grav)
 
 
 static void
-ns_set_window_size (struct frame *f,
-                    bool change_gravity,
-                    int width,
-                    int height)
+ns_set_window_size (struct frame *f, bool change_gravity,
+                    int width, int height)
 /* --------------------------------------------------------------------------
      Adjust window pixel size based on native sizes WIDTH and HEIGHT.
      Impl is a bit more complex than other terms, need to do some
@@ -8729,17 +8727,18 @@ ns_create_font_panel_buttons (id target, SEL select, 
SEL cancel_action)
 @implementation EmacsWindow
 
 
-- (instancetype) initWithEmacsFrame:(struct frame *)f
+- (instancetype) initWithEmacsFrame: (struct frame *) f
 {
   return [self initWithEmacsFrame:f fullscreen:NO screen:nil];
 }
 
 
-- (instancetype) initWithEmacsFrame:(struct frame *)f
-                         fullscreen:(BOOL)fullscreen
-                             screen:(NSScreen *)screen
+- (instancetype) initWithEmacsFrame: (struct frame *) f
+                         fullscreen: (BOOL) fullscreen
+                             screen: (NSScreen *) screen
 {
   NSWindowStyleMask styleMask;
+  int width, height;
 
   NSTRACE ("[EmacsWindow initWithEmacsFrame:fullscreen:screen:]");
 
@@ -8752,20 +8751,22 @@ ns_create_font_panel_buttons (id target, SEL select, 
SEL cancel_action)
       styleMask |= NSWindowStyleMaskResizable;
 #endif
     }
+  else if (f->tooltip)
+    styleMask = 0;
   else
-    styleMask = NSWindowStyleMaskTitled
-      | NSWindowStyleMaskResizable
-      | NSWindowStyleMaskMiniaturizable
-      | NSWindowStyleMaskClosable;
-
-  self = [super initWithContentRect:
-                  NSMakeRect (0, 0,
-                              FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols),
-                              FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 
f->text_lines))
-                          styleMask:styleMask
-                            backing:NSBackingStoreBuffered
-                              defer:YES
-                             screen:screen];
+    styleMask = (NSWindowStyleMaskTitled
+                | NSWindowStyleMaskResizable
+                | NSWindowStyleMaskMiniaturizable
+                | NSWindowStyleMaskClosable);
+
+  width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols);
+  height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines);
+
+  self = [super initWithContentRect: NSMakeRect (0, 0, width, height)
+                          styleMask: styleMask
+                            backing: NSBackingStoreBuffered
+                              defer: YES
+                             screen: screen];
   if (self)
     {
       NSString *name;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]