Detection when the user has finished changing the SWT shell

I have a SWT shell that resizes. Every time it changes, I have to do something computationally intense.

I can register ControlListenerin my shell, but this generates events continuously during the resize operation, and I have no idea when the drag-and-drop mouse operation ends.

I would like to know when the user finished resizing the shell, and then initiated my intensive work using computing. Any ideas how to do this?

+3
source share
5 answers

? :

long lastEvent;

ActionListener taskPerformer = new ActionListener() {
            public void doCalc(ActionEvent evt) {
                if ( (lastEvent + 1000) < System.currentTimeMillis() ) {
                   hardcoreCalculationTask();
                } else {
                  // this can be timed better
                  new Timer(1000, taskPerformer).start();
                }
            }
        };
}

:

 lastEvent = System.currentTimeMillis();
 new Timer(1000, taskPerformer).start();
+6

: [platform-swt-dev] :

, Display.asyncExec(). , , . , .

, , , , ( ), . ...

+3

, , , , SWT API, , .

, :

private class ResizeListener implements ControlListener, Runnable, Listener {

    private long lastEvent = 0;

    private boolean mouse = true;

    public void controlMoved(ControlEvent e) {
    }

    public void controlResized(ControlEvent e) {
        lastEvent = System.currentTimeMillis();
        Display.getDefault().timerExec(500, this);
    }

    public void run() {
        if ((lastEvent + 500) < System.currentTimeMillis() && mouse) {
        ...work
        } else {
            Display.getDefault().timerExec(500, this);
        }
    }
    public void handleEvent(Event event) {
        mouse = event.type == SWT.MouseUp;
    }

}

. , . , , .

    ResizeListener listener = new ResizeListener();
    widget.addControlListener(listener);
    widget.getDisplay().addFilter(SWT.MouseDown, listener);
    widget.getDisplay().addFilter(SWT.MouseUp, listener);
+3

, Executor, "" .

(Runnables) DelayQueue, Scheduler-Thread. , , , , . , , .

String-identifier, , "".

, , .

package org.uilib.util;

import com.google.common.collect.Maps;

import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class SmartExecutor implements Throttle, Executor {

    //~ Static fields/initializers -------------------------------------------------------------------------------------

    private static final Logger L = LoggerFactory.getLogger(SmartExecutor.class);

    //~ Instance fields ------------------------------------------------------------------------------------------------

    private final ExecutorService executor                      = Executors.newCachedThreadPool();
    private final DelayQueue<DelayedRunnable> taskQueue         = new DelayQueue<DelayedRunnable>();
    private final Map<String, ThrottledRunnable> throttledTasks = Maps.newHashMap();

    //~ Constructors ---------------------------------------------------------------------------------------------------

    /* schedule a Runnable to be executed a fixed period of time after it was scheduled
     * if a new Runnable with the same throttleName is scheduled before this one was called, it will overwrite this */
    public SmartExecutor() {
        this.executor.execute(new Scheduler());
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    /* execute a Runnable once */
    @Override
    public void execute(final Runnable runnable) {
        this.executor.execute(runnable);
    }

    /* schedule a Runnable to be executed after a fixed period of time */
    public void schedule(final long delay, final TimeUnit timeUnit, final Runnable runnable) {
        this.taskQueue.put(new DelayedRunnable(runnable, delay, timeUnit));
    }

    /* schedule a Runnable to be executed using a fixed delay between the end of a run and the start of the next one */
    public void scheduleAtFixedRate(final long period, final TimeUnit timeUnit, final Runnable runnable) {
        this.taskQueue.put(new RepeatingRunnable(runnable, period, timeUnit));
    }

    /* shut the the executor down */
    public void shutdown() {
        this.executor.shutdownNow();
    }

    @Override
    public void throttle(final String throttleName, final long delay, final TimeUnit timeUnit, final Runnable runnable) {

        final ThrottledRunnable thrRunnable = new ThrottledRunnable(runnable, throttleName, delay, timeUnit);
        this.throttledTasks.put(throttleName, thrRunnable);
        this.taskQueue.put(thrRunnable);
    }

    //~ Inner Classes --------------------------------------------------------------------------------------------------

    private static class DelayedRunnable implements Delayed, Runnable {

        protected final Runnable runnable;
        private final long endOfDelay;

        public DelayedRunnable(final Runnable runnable, final long delay, final TimeUnit delayUnit) {
            this.runnable       = runnable;
            this.endOfDelay     = delayUnit.toMillis(delay) + System.currentTimeMillis();
        }

        @Override
        public int compareTo(final Delayed other) {

            final Long delay1 = this.getDelay(TimeUnit.MILLISECONDS);
            final Long delay2 = other.getDelay(TimeUnit.MILLISECONDS);

            return delay1.compareTo(delay2);
        }

        @Override
        public long getDelay(final TimeUnit unit) {
            return unit.convert(this.endOfDelay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        @Override
        public void run() {
            this.runnable.run();
        }
    }

    private static final class RepeatingRunnable extends DelayedRunnable {

        private final long periodInMillis;

        public RepeatingRunnable(final Runnable runnable, final long period, final TimeUnit delayUnit) {
            super(runnable, period, delayUnit);

            this.periodInMillis = delayUnit.convert(period, TimeUnit.MILLISECONDS);
        }

        public RepeatingRunnable reschedule() {
            return new RepeatingRunnable(this.runnable, this.periodInMillis, TimeUnit.MILLISECONDS);
        }
    }

    private final class Scheduler implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {

                    /* wait for the next runnable to become available */
                    final DelayedRunnable task = SmartExecutor.this.taskQueue.take();

                    if (task instanceof RepeatingRunnable) {
                        /* tell executor to run the action and reschedule it afterwards */
                        SmartExecutor.this.executor.execute(
                            new Runnable() {
                                    @Override
                                    public void run() {
                                        task.run();
                                        SmartExecutor.this.taskQueue.put(((RepeatingRunnable) task).reschedule());
                                    }
                                });
                    } else if (task instanceof ThrottledRunnable) {

                        final ThrottledRunnable thrTask = (ThrottledRunnable) task;

                        /* run only if this is the latest task in given throttle, otherwise skip execution */
                        if (SmartExecutor.this.throttledTasks.get(thrTask.getThrottleName()) == thrTask) {
                            SmartExecutor.this.executor.execute(task);
                        }
                    } else {
                        /* tell the executor to just run the action */
                        SmartExecutor.this.executor.execute(task);
                    }
                } catch (final InterruptedException e) {
                    SmartExecutor.L.debug("scheduler interrupted (shutting down)");
                    return;
                }
            }
        }
    }

    private static final class ThrottledRunnable extends DelayedRunnable {

        private final String throttleName;

        public ThrottledRunnable(final Runnable runnable, final String throttleName, final long period,
                                 final TimeUnit delayUnit) {
            super(runnable, period, delayUnit);

            this.throttleName = throttleName;
        }

        public String getThrottleName() {
            return this.throttleName;
        }
    }
}
+2

If the problem is blocking the user interface thread when resizing, you should consider the asyncExecclass methodDisplay

/**
 * Causes the <code>run()</code> method of the runnable to
 * be invoked by the user-interface thread at the next 
 * reasonable opportunity. The caller of this method continues 
 * to run in parallel, and is not notified when the
 * runnable has completed.  Specifying <code>null</code> as the
 * runnable simply wakes the user-interface thread when run.
 * <p>
 * Note that at the time the runnable is invoked, widgets 
 * that have the receiver as their display may have been
 * disposed. Therefore, it is necessary to check for this
 * case inside the runnable before accessing the widget.
 * </p>
 *
 * @param runnable code to run on the user-interface thread or <code>null</code>
 *
 * @exception SWTException <ul>
 *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
 * </ul>
 * 
 * @see #syncExec
 */
public void asyncExec (Runnable runnable) {
    synchronized (Device.class) {
        if (isDisposed ()) error (SWT.ERROR_DEVICE_DISPOSED);
        synchronizer.asyncExec (runnable);
    }
}
-2
source

Source: https://habr.com/ru/post/1728684/


All Articles