From 37b8da6d2986158840e40e61e44ed7cd5d6455fa Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 24 Feb 2022 07:17:14 +0100 Subject: [PATCH] Rewrite delayed text --- .../src/org/argeo/app/ui/RecentItems.java | 35 ++--- .../org/argeo/app/ui/widgets/DelayedText.java | 146 +++++++----------- 2 files changed, 77 insertions(+), 104 deletions(-) diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java b/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java index bad48b8..c4272e6 100644 --- a/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java +++ b/org.argeo.app.ui/src/org/argeo/app/ui/RecentItems.java @@ -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) { diff --git a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java index 1eab6d6..ecf6639 100644 --- a/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java +++ b/org.argeo.app.ui/src/org/argeo/app/ui/widgets/DelayedText.java @@ -1,117 +1,91 @@ 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> toDos = new ArrayList<>(); + private ServerPushSession pushSession; - synchronized (timer) { - if (timer.timerTask != null) { - timer.timerTask.cancel(); - timer.timerTask = null; - } + private ScheduledFuture 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 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 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 task = scheduler.schedule(() -> { + notifyText(txt); + return txt; + }, delay, TimeUnit.MILLISECONDS); + // cancel previous task + if (lastTask != null && !lastTask.isDone()) { + lastTask.cancel(false); + } + lastTask = task; + } }; } -- 2.30.2