0

I detected a very curious phenomenon when executing script code in an SWT Browser.
I've two controls in my window: One List and one Browser, placed side by side (GridLayout.numColumns=2).

The list contains some headings which I want to navigate to later in the HTML document.

private void setFocusToElement(String id) {
  StringBuilder sb = new StringBuilder();
  sb.append("var elem = document.getElementById('" + id + "');");
  sb.append("if (elem) {");
  sb.append("  elem.setAttribute('tabindex', '-1');");
  sb.append("  elem.focus();");
  sb.append("}");

  browser.execute(sb.toString());
}

The method is called everytime I click an item in the list.
The heading is correctly selected so far the execution comes up with my expectation.

But now the focus moves without my interaction from the list to the browser.
Is this an SWT bug or is it my mistake and how can I prevent moving the focus?

+++ UPDATED +++

My list control contains ids with which I try to find and select headings h1...h6 in the browser's HTML document.
I need to set the focus on such a heading so .scrollIntoView() can't be used.

Example:

I have a list entry named "myHeading-1". I click on this item and want to select the corresponding heading in the webview (h1 with the id from the list). For this purpose I use the JavaScript code above with "myHeading-1" as input parameter.

And the result is that my list control loses the focus. That's a little bit unlogic for me because I never use browser.setFocus(). I only used the JavaScript focus function so that should not happen.

My expectation is:

The heading in the webview is selected but the focus is still placed on the list control.

Ok and here's some code to demonstrate waht I mean. Maybe then it's better to reproduce the effect:

public class Main {
  private List list;
  private Browser browser;

  public Main() {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setLayout(new GridLayout(2, false));
    shell.setSize(400, 200);
    shell.setText("Demo");

    list = new List(shell, SWT.BORDER | SWT.SINGLE);
    list.add("rtrx_001d");
    list.add("rtrx_003e");
    list.setSelection(0);
    list.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        setFocusToElement(list.getSelection()[0]);
      }
    });

    browser = new Browser(shell, SWT.NONE);
    try {
      browser.setText(readAllLines(new File("C:\\workspace\\file.html")));
    } catch (IOException e) {
      System.err.println(e);
    }

    shell.open();
    while (!shell.isDisposed()) {
      if (!display.readAndDispatch()) {
        display.sleep();
      }
    }
    display.dispose();
  }

  private String readAllLines(File file) throws IOException {
    return readAllLines(file, StandardCharsets.UTF_8);
  }

  private String readAllLines(File file, Charset charset) throws IOException {
    return readAllLines(new FileInputStream(file), charset);
  }

  private String readAllLines(InputStream istream, Charset charset) throws IOException {
    StringBuilder sb = new StringBuilder();
    BufferedReader reader = new BufferedReader(new InputStreamReader(istream, charset));
    try {
      String line;
      while ((line = reader.readLine()) != null) {
        sb.append(line);
        sb.append('\n');
      }
      return sb.toString();
    } finally {
      try {
        reader.close();
      } catch (IOException e) {
      }
    }
  }

  private void setFocusToElement(String id) {
    StringBuilder sb = new StringBuilder();
    sb.append("var elem = document.getElementById('" + id + "');");
    sb.append("if (elem) {");
    sb.append("  elem.setAttribute('tabindex', '-1');");
    sb.append("  elem.focus();");
    sb.append("}");
    browser.execute(sb.toString());
  }

  public static void main(String[] args) {
    new Main();
  }
}

And here's my HTML document:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>My page</title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
  </head>
  <body>
    <h1 class="title" id="nlzt_0001">
      <a href="nlzt0002.smil#nlzt_0001">Introduction</a>
    </h1>
    <h1 id="rtrx_001d">
      <a href="nlzt000a.smil#rtrx_001d">Summary</a>
    </h1>
    <h1 id="rtrx_001e">
      <a href="nlzt000b.smil#rtrx_001e">First chapter</a>
    </h1>
    <h2 id="rtrx_001f">
      <a href="nlzt000c.smil#rtrx_001f">1</a>
    </h2>
    <h1 id="rtrx_003e">
      <a href="nlzt002b.smil#rtrx_003e">Second chapter</a>
    </h1>
  </body>
</html>
altralaser
  • 2,035
  • 5
  • 36
  • 55
  • I can't reproduca this at all. My `List` doesn't loose focus when I set it to an element in JS. – Baz Apr 05 '16 at 13:18
  • In any case, why not simply call `list#forceFocus()` after executing the javascript? – Baz Apr 05 '16 at 13:43
  • @baz: Because the list is not the only control which loses the focus. It was only one example. But you're right - if I can't find a better way then I will check the focused control, store it in a global variable and reset the focus after executing the script code. I only thought that this focus change is a bug and looked for a solution. But if this is the normal behavior then maybe I have to get another idea. – altralaser Apr 05 '16 at 14:49
  • I can't say if it's the "normal" behaviour, because I can't reproduce it. – Baz Apr 05 '16 at 14:50
  • My program is running under Windows, IE wrapped. – altralaser Apr 06 '16 at 03:51
  • I tried your example. Without having your html file, I just made up some html with elements having the ids from the list. It still works perfectly fine. The list doesn't loose focus. To properly reproduce it, I'd need a copy of your html file, or ideally, a minimal html file that still shows the problem. – Baz Apr 06 '16 at 10:47
  • You tested the same code and with the same embedded JavaScript? Hm, very curious. Yesterday I tried it also under OS X and there the same effect occurs like under Windows. But no problem, I updated my post with an extraction of my HTML document. – altralaser Apr 08 '16 at 03:07
  • Maybe it's really so that the HTML elements were handled as UI elements like xvBTj sayed. It would be a bit confusing because then JavaScript code is mixed with SWT toolkit functions but it would be an explanation... – altralaser Apr 08 '16 at 03:10
  • Interesting. Now with that HTML I can see your problem. I've tested a bit and can only say that `list.forceFocus();` is the only thing that worked for me. – Baz Apr 08 '16 at 08:32
  • Thank you anyway. At least I know now that the problem is not only with me and it is reproducable. – altralaser Apr 11 '16 at 11:13

1 Answers1

0

Your Javascript code explicitly focuses an element with elem.focus(). This is why the browser receives the focus.

All UIs have in common that there can be at most one UI element that has the current input focus, i.e. that receives keyboard events. And an HTML element within a browser is a UI element as well. If you want the focus to stay with the list, you must not focus an HTML element within the browser.

Furthermore, my experiments have shown that giving focus to an HTML element (for example h1) not necessarily scrolls the element into view.

public static void main( String[] args ) {
  String html = "<html><head></head><body>";
  for( int i = 0; i < 100; i++ ) {
    html += "<h2 id=\"id" + i + "\">This is header " + i + "</h2>";
  }
  html += "</body></html>";
  Display display = new Display();
  Shell shell = new Shell( display );
  shell.setLayout( new GridLayout( 1, false ) );
  Button button = new Button( shell, SWT.PUSH );
  button.setText( "Focus header 17" );
  Browser browser = new Browser( shell, SWT.BORDER );
  browser.setText( html );
  browser.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) );
  button.addListener( SWT.Selection, new Listener() {
    @Override
    public void handleEvent( Event event ) {
      if( !browser.execute( "document.getElementById( 'id17' ).focus();" ) ) {
        throw new RuntimeException( "Failed to execute Javascript" );
      }
    }
  } );
  shell.open();
  while( !shell.isDisposed() ) {
    if( !display.readAndDispatch() )
      display.sleep();
  }
  display.dispose();
}

In order to scroll an element into view (without giving focus) see here: How do I scroll to an element using JavaScript?

Community
  • 1
  • 1
Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
  • Ok, but why the script focus has the same effect like the SWT.Control focus? In my opinion these should be two different things: embeded JavaScript on the one side and a toolkit function on the other side. Your idea using .scrollIntoView() was good but the problem is that I need explicitly the focus on a specific heading. With .scrollIntoView() the focus is still set to the first text line. That's why I thought it's better to use .focus(). – altralaser Apr 05 '16 at 10:13
  • So then your problem is that the browser receives focus instead of the HTML element? What kind of element (div, input, a, ...) do you try to focus? – Rüdiger Herrmann Apr 05 '16 at 10:41
  • For example

    ...

    – altralaser Apr 05 '16 at 11:15
  • Please see my edited answer. If that doesn't answer your question you may want to edit your question and clarify what the expected behavior should be when selecting an item from the list. – Rüdiger Herrmann Apr 05 '16 at 12:05