Home » Eclipse Projects » Eclipse Scout » Update the value of a field during its own execChangedValue() event.(org.eclipse.scout.rt.client.ui.form.fields.AbstractValueField.setValue(AbstractValueField.java:258) Loop detection in ***Field with value ***)
| | |
Re: Update the value of a field during its own execChangedValue() event. [message #1439947 is a reply to message #1439631] |
Tue, 07 October 2014 18:27 |
Jeremie Bresson Messages: 1252 Registered: October 2011 |
Senior Member |
|
|
Here some input I got:
System-Jobs are Jobs that are not visible to the user (They are not displayed in the Job View).
(In our project, we have removed the "Job View" completely).
A typical Client-Sync-Job freezes the user interface, but after a delay.
With the SwtBusyHandler, it is possible to influence this behavior.
For example, it is possible to freeze the whole workbench or just a part of it.
If the whole Workbench is blocked, Eclipse adds a red stop button in the status bar. (See the screenshot below and/or this discussion: Stop Button / "Cancel Current Operation")
Here is an example how you can freeze the whole workbench:
Add your own SwtBusyHandler - create a class:
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.scout.rt.client.IClientSession;
import org.eclipse.scout.rt.shared.TEXTS;
import org.eclipse.scout.rt.ui.swt.ISwtEnvironment;
import org.eclipse.scout.rt.ui.swt.busy.SwtBusyHandler;
import org.eclipse.scout.rt.ui.swt.busy.strategy.workbench.BlockWorkbenchJob;
public class MySwtBusyHandler extends SwtBusyHandler {
public MySwtBusyHandler(IClientSession session, ISwtEnvironment env) {
super(session, env);
}
@Override
protected void runBusy(Job job) {
BlockWorkbenchJob busyWaitJob = new BlockWorkbenchJob(TEXTS.get("BusyJob"), this);
busyWaitJob.setSystem(true);
busyWaitJob.setUser(false);
busyWaitJob.schedule();
}
}
In the SwtEnvironment of your application, override the createBusyHandler() method. Instead of using the default busy handler, you now instantiate your specific BusyHandler created previously:
@Override
protected SwtBusyHandler createBusyHandler() {
return new MySwtBusyHandler(getClientSession(), this);
}
My first tests (on a small PoC application) look promising.
One issue still need to be fixed:
When the form is opened (as view in my case), if the load operation (in ModifyHandler.execLoad()) takes a long time, the form do not have the focus when it appears.
This is a problem for our user.
- A form without focus cannot be closed with the "escape" KeyStroke we have in our forms
- Our users want to start working with the form, without having to TAB until the first field is reached.
I hope we will manage to fix this last issue.
|
|
|
Re: Update the value of a field during its own execChangedValue() event. [message #1451065 is a reply to message #1362156] |
Thu, 23 October 2014 09:21 |
Daniel Wiehl Messages: 1 Registered: May 2016 |
Junior Member |
|
|
Hi Jeremie
The time the workbench is blocked, the focus request is ignored because the workbench filters the resulting OS-event from the SWT event queue. Actually, this is exactly the behaviour wanted to prevent the user from interacting with your application.
To solve your problem, you have to delay requesting the focus until the workbench is not blocked anymore. The easiest way to do that is by contributing your own implementation of SwtScoutForm#handleRequestFocusFromScout. There, one possible way could be to try to set the focus repeatedly as long as not being accepted by the UI. Obviously, this is not good practice and it would be way better to only set the focus once the workbench is not blocked anymore. If having a look at IBusyHandler, you may notice that there is a state-lock you can wait for to get awaken once the long-running operation completes. Unfortunately, this will not work very reliable because being the same trigger for also exiting the workbench blocking mode as well.
As a consequence, you have to introduce your own BusyJob and BusyHandler with your BusyHandler firing a notification once the blocking mode is exited which is shortly after the long-running operation completed. With that mechanism installed, you finally can wait for that event in
SwtScoutForm#handleRequestFocusFromScout and only set the focus if being in non-blocked state.
In your previous post you presented a solution of how to block the workbench immediately when running a ClientSyncJob. Actually, this might lead to unexpected side effects. Imagine, you have a FormField that validates on every key. Every time you type in some characters, the workbench is blocked and the focus restored afterwards. However, the field is focused anew and you lose the current caret position. To face that problem, BusyJob is divided into 2 temporal timed phases: In the first phase, typically a busy cursor is present but the workbench not blocked yet. Only if the operation takes some more time, the workbench gets blocked. That is why I suggest you not to bypass the first phase but to reduce the duration to detect long-running operations instead.
Another point to consider is that when requesting the initial focus in AbstractFormHandler #execLoad, the Scout model is not attached to the UI yet, meaning that the focus cannot be applied yet. Once the model is attached to the UI, the event-history is looked for such a focus request which is applied if present. The point is that this history only keeps events for some maximum period of time (by default this is 5 seconds). To preserve events longer in history, you have to set a bigger expiration duration in AbstractForm#createEventHistory or more easily, simply request the focus at the very end of your execLoad method instead.
In the following, please find the changes required:
If you like to have that behavior in Scout, please file a bug on Bugzilla.
Use your own SwtBusyHandler:
/**
* Handler to block the workbench for long-running operations and to provide feedback if the workbench is
* accessible again.
*
* @see WorkbenchBlockingJobEx
* @see #waitForWorkbenchBlockingToComplete()
*/
public class SwtBusyHandlerEx extends SwtBusyHandler {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwtBusyHandlerEx.class);
private final Object m_blockingLock = new Object();
private int m_blockingCount;
public SwtBusyHandlerEx(IClientSession session, ISwtEnvironment env) {
super(session, env);
// configure the timeout after which the workbench should block to a reasonable value.
setLongOperationMillis(300);
}
@Override
protected void runBusy(Job job) {
new WorkbenchBlockingJobEx(this).schedule();
}
/**
* Blocks the caller as long as the workbench is blocked by a long-runnning operation; has no effect if there is no
* long-running operation in progress.
*/
public void waitForWorkbenchBlockingToComplete() {
synchronized (m_blockingLock) {
while (m_blockingCount > 0) {
try {
m_blockingLock.wait();
}
catch (InterruptedException e) {
LOG.warn("Interrupted while waiting for the workbench not to be blocked anymore.");
}
}
}
}
/**
* Notify the handler about start blocking the workbench.
*/
void notifyWorkbenchBlockingStarted() {
synchronized (m_blockingLock) {
m_blockingCount++;
}
}
/**
* Notify the handler about not blocking the workbench anymore.
*/
void notifyWorkbenchBlockingStopped() {
synchronized (m_blockingLock) {
if (m_blockingCount == 0) {
return;
}
m_blockingCount--;
// notify
if (m_blockingCount == 0) {
m_blockingLock.notifyAll();
}
}
}
}
Use your own BusyJob:
/**
* Blocks the Workbench during the execution of long-running operations to prevent the user from interacting
* with the application. As long as the <code>shortTimeMillis</code> timeout is not reached, a busy cursor is
* shown and the workbench not blocked yet.
*
* @see {@link IBusyHandler#getShortOperationMillis()}
* @see {@link IBusyHandler#getLongOperationMillis()}
*/
public class WorkbenchBlockingJobEx extends BusyJob {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(WorkbenchBlockingJobEx.class);
public WorkbenchBlockingJobEx(SwtBusyHandler handler) {
super(TEXTS.get("BusyJob"), handler);
setSystem(false);
setUser(false);
}
@Override
protected SwtBusyHandlerEx getBusyHandler() {
return (SwtBusyHandlerEx) super.getBusyHandler();
}
@Override
protected void runBusy(IProgressMonitor monitor) {
// Show a busy cursor during execution of short-running operations.
IRunnableWithProgress busyRunnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor2) throws InvocationTargetException, InterruptedException {
WorkbenchBlockingJobEx.super.runBusy(monitor2);
}
};
SwtBusyUtility.showBusyIndicator(getBusyHandler(), busyRunnable, monitor);
}
@Override
protected void runBlocking(IProgressMonitor monitor) {
SwtBusyHandlerEx handler = getBusyHandler();
// Block the workbench during execution of long-running operations.
IRunnableWithProgress blockingRunnable = new IRunnableWithProgress() {
@Override
public void run(IProgressMonitor monitor2) throws InvocationTargetException, InterruptedException {
WorkbenchBlockingJobEx.super.runBlocking(monitor2);
}
};
handler.notifyWorkbenchBlockingStarted(); // notify the handler about start blocking the workbench.
try {
SwtBusyUtility.showWorkbenchIndicator(handler, blockingRunnable);
}
finally {
handler.notifyWorkbenchBlockingStopped(); // notify the handler about not blocking the workbench anymore.
}
}
}
Change the behaviour of 'Focus-Request' in SwtScoutForm:
/**
* SwtScoutForm with support for setting the focus control even if the workbench is blocked and therefore would
* not accept focus requests.
*/
public class SwtScoutFormEx extends SwtScoutForm {
private static IScoutLogger LOG = ScoutLogManager.getLogger(SwtScoutFormEx.class);
@Override
protected void handleRequestFocusFromScout(final IFormField modelField, final boolean force) {
// This request is run on behalf of the SWT-Thread.
// Get the installed busy handler to request the focus only if the workbench is not blocked.
// Otherwise, the focus request would not be accepted as filtered in blocking state by the workbench.
final IClientSession clientSession = getEnvironment().getClientSession();
final IBusyHandler busyHandler = SERVICES.getService(IBusyManagerService.class).getHandler(clientSession);
if (busyHandler instanceof SwtBusyHandlerEx) {
// Asynchronously wait for the workbench to be accessible without blocking the SWT-Thread.
Job job = new Job("Wait for blocked workbench to be complete") {
@Override
protected IStatus run(IProgressMonitor monitor) {
// Wait for the workbench to be accessible to accept focus requests.
((SwtBusyHandlerEx) busyHandler).waitForWorkbenchBlockingToComplete();
// Synchronize with SWT-Thread to request the focus.
getEnvironment().getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
if (isDisposed()) {
return; // NOOP, weil Form bereits geschlossen.
} else {
// Request the focus.
SwtScoutFormEx.super.handleRequestFocusFromScout(modelField, force);
}
}
});
return Status.OK_STATUS;
}
};
job.setSystem(true);
job.setUser(false);
job.schedule();
}
else {
SwtScoutFormEx.super.handleRequestFocusFromScout(modelField, force);
}
}
}
Register your BusyHandler und SwtScoutForm in SwtEnvironment:
@Override
public ISwtScoutForm createForm(Composite parent, IForm scoutForm) {
// Create SwtScoutForm with support for setting focus in blocking workbench state.
SwtScoutForm uiForm = new SwtScoutFormEx();
uiForm.createField(parent, scoutForm, this);
assignWidgetId(scoutForm, uiForm.getSwtField(), uiForm.getSwtContainer());
return uiForm;
}
@Override
protected SwtBusyHandler createBusyHandler() {
// Create BusyHandler with functionality to wait for long-running operations to complete.
return new SwtBusyHandlerEx(ClientSession.get(), this);
}
Update 03.06.2015: modification of handleRequestFocusFromScout(..) [fixed a bug]
[Updated on: Wed, 03 June 2015 15:34] by Moderator Report message to a moderator
|
|
| |
Goto Forum:
Current Time: Fri Mar 29 10:26:45 GMT 2024
Powered by FUDForum. Page generated in 0.04575 seconds
|