changeset 27738:7fff532afcb1

legend.m: Fix handling of empty strings and displayname (bug #57271) * legend.m: Don't systematicaly include "string" in the list of property values. (legend_autoupdate_cb): better handling of new/deleted children labels. (diplayname_or_defatul): Reuse old strings if present. (parse_opts): leave "obj_labels" field empty if the string hasn't changed. (textitem_objects): Add listener on the "displayname".
author Pantxo Diribarne <pantxo.diribarne@gmail.com>
date Sat, 23 Nov 2019 13:09:50 +0100
parents 527e25f7ee38
children f18f85ec4b96
files scripts/plot/appearance/legend.m
diffstat 1 files changed, 70 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/plot/appearance/legend.m	Fri Nov 22 20:36:05 2019 +0100
+++ b/scripts/plot/appearance/legend.m	Sat Nov 23 13:09:50 2019 +0100
@@ -129,7 +129,7 @@
   hl = opts.legend_handle;
 
   ## Fix property/value pairs
-  pval = ["string", {opts.obj_labels}, opts.propval(:)'];
+  pval = opts.propval(:)';
 
   if (! isempty (opts.action))
 
@@ -202,7 +202,7 @@
     ## Add and update legend specific properties
     addproperties (hl);
     try
-      set (hl, pval{:});
+      set (hl, "string", opts.obj_labels, pval{:});
     catch ee
       delete (hl);
       set (opts.axes_handles, "__legend_handle__", []);
@@ -278,7 +278,11 @@
 
     ## Update properties
     setappdata (hl, "__peer_objects__", opts.obj_handles);
-    set (hl, pval{:});
+    if (! isempty (opts.obj_labels))
+      set (hl ,"string", opts.obj_labels, pval{:})
+    elseif (! isempty (pval))
+      set (hl, pval{:});
+    endif
 
   endif
 
@@ -518,13 +522,12 @@
     kids = get (hax, "children");
   end_try_catch
 
-  is_deletion = getappdata (hl, "__total_num_children__") > numel (kids);
+  is_deletion = (getappdata (hl, "__total_num_children__") > numel (kids));
   setappdata (hl, "__total_num_children__", numel (kids));
 
   ## Remove item for deleted object
   current_obj = getappdata (hl, "__peer_objects__");
   [~, iold, inew] = setxor (current_obj, kids);
-  kids = kids(inew);
   current_obj(iold) = [];
 
   if (isempty (current_obj))
@@ -534,17 +537,14 @@
 
   if (! is_deletion && strcmp (get (hl, "autoupdate"), "on"))
 
-    ## Add item for the latest created object
-    persistent valid_types = {"line", "patch", "surface", "hggroup"};
-    valid = arrayfun (@(h) any (strcmp (get (h, "type"), valid_types)), kids);
-    kids(! valid) = [];
+    ## We only expect 1 new child
+    kids = kids(min (inew));
 
     ## FIXME: if the latest child is an hggroup, we cannot label it since this
     ## function is called before the hggroup has been properly populated.
-    if (numel (kids) > 0 && strcmp (get (kids(1), "type"), "hggroup"))
+    persistent valid_types = {"line", "patch", "surface"};
+    if (! any (strcmp (get (kids, "type"), valid_types)))
       kids = [];
-    elseif (numel (kids) > 1)
-      kids = kids(1);
     endif
 
   else
@@ -734,7 +734,13 @@
       endif
     endif
   else
-    [obj_labels, next_idx] = displayname_or_default (obj_handles);
+    [tmp_labels, next_idx] = displayname_or_default (obj_handles,
+                                                     legend_handle);
+    if (isempty (legend_handle)
+        || ! isequal (tmp_labels, get (legend_handle, "string")))
+      obj_labels = tmp_labels;
+    endif
+    
   endif
 
   opts.action = action;
@@ -754,20 +760,43 @@
     next_idx = getappdata (hl, "__next_label_index__");
   endif
 
+  ## Use the displayname property
   labels = get (hplots, "displayname");
   if (! iscell (labels))
     labels = {labels};
   endif
 
-  idx = cellfun (@isempty, labels);
-  if (any (idx))
-    default = arrayfun (@(ii) sprintf ("data%d", ii), ...
-                        [next_idx:(next_idx + sum (idx) - 1)], ...
-                        "uniformoutput", false)(:);
-    labels(idx) = default;
+  ## Fallback to automatic names for empty labels
+  empty_label_idx = cellfun (@isempty, labels);
+
+  if (any (empty_label_idx) && ! isempty (hl))
+    ## Empty strings must not be blindly replaced by data%d. If there exist
+    ## an old text item that was affected an empty string, keep it as is.
+    kids = get (hl, "children");
+    htext = kids(strcmp (get (kids, "type"), "text"));
+    old_objects = get (htext, "peer_object");
+    if (iscell (old_objects))
+      old_objects = cell2mat (old_objects);
+    endif
+
+    for h = hplots(empty_label_idx).'
+      idx = (h == old_objects);
+      if (any (idx))
+        labels(hplots == h) = get (htext(idx), "string");
+        empty_label_idx(hplots == h) = false;
+      endif
+    endfor
+
   endif
 
-  next_idx += sum (idx);
+  if (any (empty_label_idx))
+    default = arrayfun (@(ii) sprintf ("data%d", ii), ...
+                        [next_idx:(next_idx + sum (empty_label_idx) - 1)], ...
+                        "uniformoutput", false)(:);
+    labels(empty_label_idx) = default;
+  endif
+
+  next_idx += sum (empty_label_idx);
 
   if (! isempty (hl))
     setappdata (hl, "__next_label_index__", next_idx);
@@ -944,6 +973,9 @@
                      @(h) set (hmarker, "xdata", mean (get (h, "xdata"))));
         addlistener (hmarker, "markersize", @update_marker_cb);
       endif
+
+      add_safe_listener (hl, hplt, "displayname", ...
+                         {@update_displayname_cb, hl});
     endif
 
     objlist(ii,:) = [htxt, hitem];
@@ -951,6 +983,24 @@
 
 endfunction
 
+function update_displayname_cb (h, ~, hl)
+
+  updating = getappdata (hl, "__updating_layout__");
+  if (! isempty (updating) && updating)
+    return;
+  endif
+
+  str = get (hl, "string");
+  if (! iscell (str))
+    str = {str};
+  endif
+
+  str{h == getappdata (hl, "__peer_objects__")} = get (h, "displayname");
+
+  set (hl ,"string", str);
+
+endfunction
+
 function update_marker_cb (h)
 
   if (get (h, "markersize") > 6)