Rewrite delayed text
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 24 Feb 2022 06:17:14 +0000 (07:17 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 24 Feb 2022 06:17:14 +0000 (07:17 +0100)
org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java
org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java

index bad48b8fb6673091d967b41d82a4a010bf03c9e0..c4272e6db7cfd0e6e5ab8bffb1963191bf12f7a0 100644 (file)
@@ -38,8 +38,6 @@ import org.eclipse.jface.viewers.Viewer;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.KeyEvent;
 import org.eclipse.swt.events.KeyListener;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
@@ -177,19 +175,20 @@ public class RecentItems implements CmsUiProvider {
                        filterTxt.setLayoutData(EclipseUiUtils.fillWidth());
 
                        // final ServerPushSession pushSession = new ServerPushSession();
-                       delayedText.addDelayedModifyListener(null, new ModifyListener() {
-                               private static final long serialVersionUID = 5003010530960334977L;
-
-                               public void modifyText(ModifyEvent event) {
-                                       delayedText.getText().getDisplay().asyncExec(new Runnable() {
-                                               @Override
-                                               public void run() {
-                                                       refreshFilteredList();
-                                               }
-                                       });
-                                       // pushSession.stop();
-                               }
-                       });
+                       delayedText.addListener((s) -> refreshFilteredList());
+//                     delayedText.addDelayedModifyListener(null, new ModifyListener() {
+//                             private static final long serialVersionUID = 5003010530960334977L;
+//
+//                             public void modifyText(ModifyEvent event) {
+//                                     delayedText.getText().getDisplay().asyncExec(new Runnable() {
+//                                             @Override
+//                                             public void run() {
+//                                                     refreshFilteredList();
+//                                             }
+//                                     });
+//                                     // pushSession.stop();
+//                             }
+//                     });
 
                        // Jump to the first item of the list using the down arrow
                        filterTxt.addKeyListener(new KeyListener() {
@@ -214,9 +213,9 @@ public class RecentItems implements CmsUiProvider {
                                }
                        });
 
-                       parent.addDisposeListener((e) -> {
-                               delayedText.close();
-                       });
+//                     parent.addDisposeListener((e) -> {
+//                             delayedText.close();
+//                     });
                }
 
                protected TableViewer createListPart(Composite parent, ILabelProvider labelProvider) {
index 1eab6d6a9c359a92a9a5a33b6c4683069cf4541f..ecf66396868364ba893c187a5dbf0014babe04bd 100644 (file)
 package org.argeo.app.ui.widgets;
 
-import java.util.Timer;
-import java.util.TimerTask;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 import org.eclipse.rap.rwt.service.ServerPushSession;
 import org.eclipse.swt.events.ModifyEvent;
 import org.eclipse.swt.events.ModifyListener;
 import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Text;
 
 /**
- * Text that introduce a timer in the attached ModifyListener.
- * 
- * Note that corresponding ModifyEvent will *NOT* be sent in the UI thread.
- * Calling ModifierInstance must be implemented in consequence. Note also that
- * this delayed text only manages one listener at a time.
- *
+ * A text input which notifies changes after a delay, typically in order to
+ * apply a filter.
  */
 public class DelayedText {
-       final int delay;
-       private Object lock = new Object();
-       private MyTimer timer = new MyTimer(DelayedText.this.toString());
-       private ModifyListener delayedModifyListener;
-       private ServerPushSession pushSession;
-
-       private Text text;
-
-       private ModifyListener modifyListener = new ModifyListener() {
-               private static final long serialVersionUID = 1117506414462641980L;
+       private final static ScheduledExecutorService scheduler;
+       static {
+               // create only one scheduler, in order not to exhaust threads
+               scheduler = Executors.newScheduledThreadPool(0, (r) -> {
+                       Thread thread = new Thread(r, "Delayed text scheduler");
+                       // we mark threads as deamons so that the shutdown hook is triggered
+                       thread.setDaemon(true);
+                       return thread;
+               });
+               Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+                       scheduler.shutdown();
+               }, "Shutdown delayed text scheduler"));
+       }
+       private final static int DEFAULT_DELAY = 800;
 
-               public void modifyText(ModifyEvent e) {
-                       ModifyEvent delayedEvent = null;
-                       synchronized (lock) {
-                               if (delayedModifyListener != null) {
-                                       Event tmpEvent = new Event();
-                                       tmpEvent.widget = text;
-                                       tmpEvent.display = e.display;
-                                       tmpEvent.data = e.data;
-                                       tmpEvent.time = e.time;
-                                       delayedEvent = new ModifyEvent(tmpEvent);
-                               }
-                       }
-                       final ModifyEvent timerModifyEvent = delayedEvent;
+       private final long delay;
+       private final InternalModifyListener modifyListener;
+       private final Text text;
+       protected List<Consumer<String>> toDos = new ArrayList<>();
+       private ServerPushSession pushSession;
 
-                       synchronized (timer) {
-                               if (timer.timerTask != null) {
-                                       timer.timerTask.cancel();
-                                       timer.timerTask = null;
-                               }
+       private ScheduledFuture<String> lastTask;
 
-                               if (delayedEvent != null) {
-                                       timer.timerTask = new TimerTask() {
-                                               public void run() {
-                                                       synchronized (lock) {
-                                                               delayedModifyListener.modifyText(timerModifyEvent);
-                                                       }
-                                                       synchronized (timer) {
-                                                               timer.timerTask = null;
-                                                       }
-                                               }
-                                       };
-                                       timer.schedule(timer.timerTask, delay);
-                                       if (pushSession != null)
-                                               pushSession.start();
-                               }
-                       }
-               };
-       };
+       public DelayedText(Composite parent, int style) {
+               this(parent, style, DEFAULT_DELAY);
+       }
 
-       public DelayedText(Composite parent, int style, int delayInMs) {
-               // super(parent, style);
-               text = new Text(parent, style);
+       public DelayedText(Composite parent, int style, long delayInMs) {
                this.delay = delayInMs;
+               this.modifyListener = new InternalModifyListener();
+               pushSession = new ServerPushSession();
+               pushSession.start();
+               text = new Text(parent, style);
                text.addModifyListener(modifyListener);
        }
 
-       /**
-        * Adds a modify text listener that will be delayed. If another Modify event
-        * happens during the waiting delay, the older event will be cancelled an a new
-        * one will be scheduled after another new delay.
-        */
-       public void addDelayedModifyListener(ServerPushSession pushSession, ModifyListener listener) {
-               synchronized (lock) {
-                       delayedModifyListener = listener;
-                       this.pushSession = pushSession;
+       protected void notifyText(String txt) {
+               // text.getDisplay().syncExec(()-> pushSession.start());
+               for (Consumer<String> toDo : toDos) {
+                       text.getDisplay().syncExec(() -> toDo.accept(txt));
                }
+               // text.getDisplay().syncExec(()->pushSession.stop());
        }
 
-       public void removeDelayedModifyListener(ModifyListener listener) {
-               synchronized (lock) {
-                       delayedModifyListener = null;
-                       pushSession = null;
-               }
+       public Text getText() {
+               return text;
        }
 
-       private class MyTimer extends Timer {
-               private TimerTask timerTask = null;
-
-               public MyTimer(String name) {
-                       super(name);
-               }
+       public void addListener(Consumer<String> toDo) {
+               toDos.add(toDo);
        }
 
-       public Text getText() {
-               return text;
-       }
+       private class InternalModifyListener implements ModifyListener {
+               private static final long serialVersionUID = -6178431173400385005L;
 
-       public void close() {
-               if (pushSession != null)
-                       pushSession.stop();
-               if (timer != null)
-                       timer.cancel();
+               public void modifyText(ModifyEvent e) {
+                       String txt = text.getText();
+                       ScheduledFuture<String> task = scheduler.schedule(() -> {
+                               notifyText(txt);
+                               return txt;
+                       }, delay, TimeUnit.MILLISECONDS);
+                       // cancel previous task
+                       if (lastTask != null && !lastTask.isDone()) {
+                               lastTask.cancel(false);
+                       }
+                       lastTask = task;
+               }
        };
 
 }