From b06ca0de72b84b1f6e13b863f510c311bd759090 Mon Sep 17 00:00:00 2001
From: Ivica Ico Bukvic <ico@vt.edu>
Date: Sun, 20 Sep 2020 23:26:31 -0400
Subject: [PATCH] Fixed asynchronoous getscroll and activate regression

* This was caused by the improvement in the scrolling algorithm earlier this summer to throttle down getscroll calls invoked by arrays that resulted in CPU intensive redraws. As it turns out, the same also up until this patch prevented the following from working properly:

1. select text object and displace it by dragging using a mouse until it goes partially outside the canvas edge

2. the object is activated at a location before getscroll changes the viewport settings to ideally fit everything within the window

3. getscroll is called and everything is relocated except the activated box because its current implementation is oblivious to the changes in canvas positioning.

4. as a result the activated box does not match the actual object position.

* The new patch addresses this by introducing a synchronous scroll request that has no delays and executes immediately, thereby completing canvas relocation before the activated box is drawn. Due to its potential to be misused causing high CPU usage, this call is currently reserved only for this special case.

* LATER: consider ensuring that activated boxes are also relocated on getscroll, so that in the case of scripted changes to the viewport activated boxes remain in a correct place. Even so, this should not be seen as a workaround but as a feature that may prove useful in other contexts. So, the solution is to have both this patch and this other thing eventually implemented.
---
 pd/nw/pdgui.js      | 15 +++++++++++++++
 pd/src/g_all_guis.c | 11 +++++++++++
 pd/src/g_all_guis.h |  1 +
 pd/src/g_editor.c   |  2 +-
 4 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 7e90a2594..2e6feb868 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -6791,6 +6791,21 @@ function gui_canvas_get_overriding_scroll(cid) {
 
 exports.gui_canvas_get_overriding_scroll = gui_canvas_get_overriding_scroll;
 
+/* ico@vt.edu 20200920: this last variant that executes immediately
+   is needed for g_text.c when one displaces a text object and it
+   immediately activates and it falls outside the visible canvas bounds
+   this can trigger the object to have its activated box at an incorrect
+   location due to asynchronous behavior of other getscroll calls. Having
+   it here as a separate call as it may prove useful later in other contexts.
+*/
+
+function gui_canvas_get_immediate_scroll(cid) {
+    //post("gui_canvas_get_immediate_scroll");
+    do_getscroll(cid, 0);
+}
+
+exports.gui_canvas_get_immediate_scroll = gui_canvas_get_immediate_scroll;
+
 function do_optimalzoom(cid, hflag, vflag) {
     // determine an optimal zoom level that makes the entire patch fit within
     // the window
diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c
index 1af9841ae..dc6fced19 100644
--- a/pd/src/g_all_guis.c
+++ b/pd/src/g_all_guis.c
@@ -1474,6 +1474,17 @@ void scrollbar_update(t_glist *glist)
     canvas_getscroll(canvas);
 }
 
+/* ico@vt.edu 20200920: introduced for situation where getscroll
+needs to occur before the next command, e.g. automate. */
+void scrollbar_synchronous_update(t_glist *glist)
+{
+    // glist_getcanvas is probably not needed but not before we make
+    // sure that there are unneded calls of this kind being made by
+    // non-toplevel objects...
+    gui_vmess("gui_canvas_get_immediate_scroll",
+        "x", glist_getcanvas(glist));
+}
+
 void wb_init(t_widgetbehavior *wb, t_getrectfn gr, t_clickfn cl)
 {
     wb->w_getrectfn = gr;
diff --git a/pd/src/g_all_guis.h b/pd/src/g_all_guis.h
index d7dec8359..813d16604 100644
--- a/pd/src/g_all_guis.h
+++ b/pd/src/g_all_guis.h
@@ -285,6 +285,7 @@ EXTERN const char *iemgui_typeface(t_iemgui *x);
 
 EXTERN void iemgui_class_addmethods(t_class *c);
 EXTERN void scrollbar_update(t_glist *glist);
+EXTERN void scrollbar_synchronous_update(t_glist *glist);
 EXTERN void iemgui_init(t_iemgui *x, t_floatarg f);
 
 EXTERN void iemgui_out_bang(t_iemgui *x, int o, int chk_putin);
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 50f8d035c..86981dce5 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -5216,12 +5216,12 @@ void canvas_mouseup(t_canvas *x,
     {
             /* after motion or resizing, if there's only one text item
                 selected, activate the text */
+        scrollbar_synchronous_update(x);
         if (x->gl_editor->e_selection &&
             !(x->gl_editor->e_selection->sel_next))
         {
             gobj_activate(x->gl_editor->e_selection->sel_what, x, 1);
         }
-        scrollbar_update(x);
     }
     else if (x->gl_editor->e_onmotion == MA_SCROLL)
     {
-- 
GitLab