[gimp] Issue #5043: Spyrogimp 'Live' Preview Pollutes Undo History



commit 17b701a94062f1d7faef1ba93403d9bab6f59337
Author: Elad Shahar <dawn ever gmail com>
Date:   Thu Jul 9 00:54:30 2020 +0300

    Issue #5043: Spyrogimp 'Live' Preview Pollutes Undo History
    
    Reduce number of undo steps created by Spryrogimp previews.
    First, by grouping them into a single undo.
    The implementation adds a function for clearing the
    incremental preview drawing that is being executed as an idle task,
    and closing an undo group in case the preview left it open (which
    would usually be the case). A second way to reduce undos is
    by generating a new preview only upon mouse button release of
    scales in the UI, and not while dragging the scale.

 plug-ins/python/spyro-plus.py | 66 ++++++++++++++++++++++++++++---------------
 1 file changed, 43 insertions(+), 23 deletions(-)
---
diff --git a/plug-ins/python/spyro-plus.py b/plug-ins/python/spyro-plus.py
old mode 100644
new mode 100755
index 1af57c951c..a316cf6bd2
--- a/plug-ins/python/spyro-plus.py
+++ b/plug-ins/python/spyro-plus.py
@@ -1349,6 +1349,17 @@ class SpyroWindow():
             self.scale.set_sensitive(val)
             self.spin.set_sensitive(val)
 
+    def on_adj_changed(self, widget):
+        self.adj_changed = True
+
+    def on_adj_release(self, widget, event, callback):
+        # Force update to accommodate manually typing numbers into the entry.
+        if isinstance(widget, Gtk.SpinButton):
+            widget.update()
+        if self.adj_changed:
+            callback(widget)
+        self.adj_changed = False
+
     def __init__(self, img, layer):
 
         def add_horizontal_separator(vbox):
@@ -1392,7 +1403,10 @@ class SpyroWindow():
             spin.set_width_chars(5)
             table.attach(spin, col, row, 1, 1)
             spin.show()
-            adj.connect("value_changed", callback)
+            adj.connect("value_changed", self.on_adj_changed)
+            spin.connect("button_release_event", self.on_adj_release, callback)
+            spin.connect("activate", self.on_adj_release, None, callback)
+            spin.connect("focus_out_event", self.on_adj_release, callback)
             return spin
 
         def hscale_in_table(adj, table, row, callback, digits=0, col=1, cols=1):
@@ -1400,10 +1414,6 @@ class SpyroWindow():
             scale = Gtk.Scale.new(Gtk.Orientation.HORIZONTAL, adj)
             scale.set_size_request(150, -1)
             scale.set_digits(digits)
-            # TODO: gtk_range_set_update_policy() has been removed in
-            # GTK+3. If we want updates to happen when button is
-            # released, we must implement this ourselves.
-            #scale.set_update_policy(Gtk.UPDATE_DISCONTINUOUS)
             scale.set_hexpand(True)
             scale.set_halign(Gtk.Align.FILL)
             table.attach(scale, col, row, cols, 1)
@@ -1417,7 +1427,11 @@ class SpyroWindow():
             table.attach(spin, col + cols, row, 1, 1)
             spin.show()
 
-            adj.connect("value_changed", callback)
+            adj.connect("value_changed", self.on_adj_changed)
+            scale.connect("button_release_event", self.on_adj_release, callback)
+            spin.connect("button_release_event", self.on_adj_release, callback)
+            spin.connect("activate", self.on_adj_release, None, callback)
+            spin.connect("focus_out_event", self.on_adj_release, callback)
 
             return self.MyScale(scale, spin)
 
@@ -1425,6 +1439,8 @@ class SpyroWindow():
             adj = Gtk.Adjustment.new(val, -180.0, 180.0, 1.0, 10.0, 0.0)
             myscale = hscale_in_table(adj, table, row, callback, digits=1)
             myscale.scale.add_mark(0.0, Gtk.PositionType.BOTTOM, None)
+            myscale.spin.set_max_length(6)
+            myscale.spin.set_width_chars(6)
             return adj, myscale
 
         def set_combo_in_table(txt_list, table, row, callback):
@@ -1812,6 +1828,8 @@ class SpyroWindow():
         self.idle_task = None
         self.enable_incremental_drawing = True
 
+        self.adj_changed = False
+
         # Draw pattern of the current settings.
         self.start_new_incremental_drawing()
 
@@ -1829,6 +1847,13 @@ class SpyroWindow():
         #GTK_RESPONSE_APPLY
         #GTK_RESPONSE_HELP
 
+    def clear_idle_task(self):
+        if self.idle_task:
+            GLib.source_remove(self.idle_task)
+            # Close the undo group in the likely case the idle task left it open.
+            self.img.undo_group_end()
+            self.idle_task = None
+
     # Callbacks for closing the plugin
 
     def ok_window(self, widget):
@@ -1858,8 +1883,7 @@ class SpyroWindow():
                 Gtk.main_quit()
         else:
             # If there is an incremental drawing taking place, lets stop it.
-            if self.idle_task:
-                GLib.source_remove(self.idle_task)
+            self.clear_idle_task()
 
             if self.spyro_layer in self.img.list_layers():
                 self.img.remove_layer(self.spyro_layer)
@@ -1877,7 +1901,7 @@ class SpyroWindow():
 
                 while self.engine.has_more_strokes():
                     yield True
-                    self.draw_next_chunk(undo_group=False, tool=tool)
+                    self.draw_next_chunk(tool=tool)
 
                 self.img.undo_group_end()
 
@@ -1887,19 +1911,18 @@ class SpyroWindow():
                 yield False
 
             tool = SaveToPathTool(self.img) if self.p.save_option == SAVE_AS_PATH else None
-
             task = draw_full(tool)
             GLib.idle_add(task.__next__)
 
     def cancel_window(self, widget, what=None):
-
-        # Note that once we call Gtk.main_quit, the idle task is stopped automatically.
+        self.clear_idle_task()
 
         # We want to delete the temporary layer, but as a precaution, lets ask first,
         # maybe it was already deleted by the user.
         if self.spyro_layer in self.img.list_layers():
             self.img.remove_layer(self.spyro_layer)
             Gimp.displays_flush()
+
         Gtk.main_quit()
 
     def update_view(self):
@@ -2058,9 +2081,10 @@ class SpyroWindow():
 
     def doughnut_changed(self, widget, hole, width):
         self.doughnut_hole_adj.set_value(hole)
+        self.p.doughnut_hole = hole
         self.doughnut_width_adj.set_value(width)
-        # We don't need to redraw, because the callbacks of the doughnut hole and
-        # width spinners will be triggered by the above lines.
+        self.p.doughnut_width = width
+        self.redraw()
 
     # Callbacks: Fixed gear
 
@@ -2133,16 +2157,12 @@ class SpyroWindow():
 
     # Incremental drawing.
 
-    def draw_next_chunk(self, undo_group=True, tool=None):
+    def draw_next_chunk(self, tool=None):
         """ Incremental drawing """
 
         t = time.time()
 
-        if undo_group:
-            self.img.undo_group_start()
         chunk_size = self.engine.draw_next_chunk(self.drawing_layer, tool=tool)
-        if undo_group:
-            self.img.undo_group_end()
 
         draw_time = time.time() - t
         self.engine.report_time(draw_time)
@@ -2166,18 +2186,18 @@ class SpyroWindow():
             self.progress_start()
             yield True
             self.engine.reset_incremental()
+
+            self.img.undo_group_start()
             while self.engine.has_more_strokes():
                 yield True
                 self.draw_next_chunk()
+            self.img.undo_group_end()
 
             self.idle_task = None
             yield False
 
-        # Remove old idle task if exists.
-        if self.idle_task:
-            GLib.source_remove(self.idle_task)
-
         # Start new idle task to perform incremental drawing in the background.
+        self.clear_idle_task()
         task = incremental_drawing()
         self.idle_task = GLib.idle_add(task.__next__)
 


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