How do you force a java swt program to "move itself to the foreground"?
Asked Answered
L

6

16

Currently with swt, I sometimes want a program to arbitrarily come to the foreground (like an alarm clock might).

Typically the following works (jruby):

@shell.setMinimized(false)
@shell.forceActive

This brings the shell to the front if it was minimized.

Creating a new shell at any time also brings the (new shell) to the front.

So far so good, however, if the shell is not minimized, the above code just flashes (blinks) the app's icon in the taskbar. Well actually the first time you run it, it brings it to the front. After that, it just blinks in the taskbar. That's windows. On Linux it appears to only blink in the taskbar (ubuntu default).

Does anybody know of a cross platform way of getting the app to come to the front, in swt?

It seems that no incantation of forceActive setActive setMinimized(false) setFocus forceFocus and setVisible can accomplish this thing.

I'm pretty sure it is possible (at least in windows), as the E Text Editor does it. Well, that's not swt, but at least some other apps have been known to do it.

I'm thinking maybe this is swt bug 192036?

Many thanks.

Related:

Laquanda answered 23/2, 2010 at 1:31 Comment(2)
Looks like the SWT bug you linked to exactly describes your problem, and it sounds like they're not going to be able to fix it.Mormon
I think that's indeed the problem for windows--good catch. Work around for now is to first minimize a shell then unminimize it (or use some native code [via ffi or jni] to forceForeGround it). In Linux I'm not quite sure what the problem is though (just blinks in the task tray). It might be fixed in newer versions of swt.jar >= 3.5 bugs.eclipse.org/bugs/show_bug.cgi?id=244597Laquanda
L
6

http://github.com/rdp/redcar/commit/d7dfeb8e77f13e5596b11df3027da236f23c83f0

shows how I did it in windows, anyway (using ffi).

A couple of helpful tricks "may" be

add a 'sleep 0.1' after the BringToFront.SetForegroundWindow(wanted) call (hopefully this one isn't actually necessary).

add a shell.set_active after you have brought the window to the foreground. For some reason forceActive doesn't call setActive.

NB that setActive does a user32.dll BringWindowToTop call, and needs to be done before you detach thread input.

Note also that it appears if you can do you calls in the right order you may not need to use the thread input hack at all (?)

http://betterlogic.com/roger/?p=2950

(contains several good hints on how to actually do this right)

On Linux, forceActive does work--but only until you move to another few windows,, then it blinks in the taskbar after that (only). Guessing swt bug. [1]

Also related:

How to bring a window to the front?

http://github.com/jarmo/win32screenshot/blob/master/lib/win32/screenshot/bitmap_maker.rb#L110 "set_foreground" which seems to work with both xp and windows 7

[1] Need to bring application to foreground on Windows and https://bugs.eclipse.org/bugs/show_bug.cgi?id=303710

Laquanda answered 23/2, 2010 at 23:43 Comment(0)
A
7

This worked for me on Windows 7 and Ubuntu:

private void bringToFront(final Shell shell) {
    shell.getDisplay().asyncExec(new Runnable() {
        public void run() {
            shell.forceActive();
        }
    });
}
Abeyant answered 2/12, 2011 at 15:53 Comment(0)
L
6

http://github.com/rdp/redcar/commit/d7dfeb8e77f13e5596b11df3027da236f23c83f0

shows how I did it in windows, anyway (using ffi).

A couple of helpful tricks "may" be

add a 'sleep 0.1' after the BringToFront.SetForegroundWindow(wanted) call (hopefully this one isn't actually necessary).

add a shell.set_active after you have brought the window to the foreground. For some reason forceActive doesn't call setActive.

NB that setActive does a user32.dll BringWindowToTop call, and needs to be done before you detach thread input.

Note also that it appears if you can do you calls in the right order you may not need to use the thread input hack at all (?)

http://betterlogic.com/roger/?p=2950

(contains several good hints on how to actually do this right)

On Linux, forceActive does work--but only until you move to another few windows,, then it blinks in the taskbar after that (only). Guessing swt bug. [1]

Also related:

How to bring a window to the front?

http://github.com/jarmo/win32screenshot/blob/master/lib/win32/screenshot/bitmap_maker.rb#L110 "set_foreground" which seems to work with both xp and windows 7

[1] Need to bring application to foreground on Windows and https://bugs.eclipse.org/bugs/show_bug.cgi?id=303710

Laquanda answered 23/2, 2010 at 23:43 Comment(0)
A
4

This is actually a feature of Windows, which can be enabled via the Tweak UI power toy (at least for Windows XP). When enabled the O/S deliberately prevents a window from forcing itself to be the focused window to stop it "stealing focus". As such, the action to grab focus is changed to just flashing the taskbar icon - since the O/S is deliberately converting the action at the user's request, there will be nothing you can do about it (and this is a good thing).

This was (probably) done because so many applications abused the bring-to-front API and the behavior both annoyed users and caused them to input into the wrong application.

Anna answered 24/2, 2010 at 7:12 Comment(2)
If you google for forceForeGround you'll see a work around, for better or worse (I assume this is what awt uses for its toFront method, of which swt doesn't appear to). I agree that forceActive should be used sparingly :)Laquanda
Ugh, yes, there are still far too many apps that pop up from the background, stealing input while you were busy typing into something else. I've experienced data loss from this before. Background app pops "Do you want to delete blah?" while I'm typing something else that happens to contain the selector for "Yes". Argh.Stamina
M
3

Bug 192036 - Shell.forceActive doesn't raise a window above all other windows

@rogerdpack's query on Eclipse bug tracker was answered with the following dirty workaround doing what we need.

public void forceActive(Shell shell) {
    int hFrom = OS.GetForegroundWindow();

    if (hFrom <= 0) {
      OS.SetForegroundWindow(shell.handle);
      return;
    }

    if (shell.handle == hFrom) {
      return;
    }

    int pid = OS.GetWindowThreadProcessId(hFrom, null);
    int _threadid = OS.GetWindowThreadProcessId(shell.handle, null);

    if (_threadid == pid) {
      OS.SetForegroundWindow(shell.handle);
      return;
    }

    if (pid > 0) {
      if ( !OS.AttachThreadInput(_threadid, pid, true)) {
        return;
      }
      OS.SetForegroundWindow(shell.handle);
      OS.AttachThreadInput(_threadid, pid, false);
    }

    OS.BringWindowToTop(shell.handle);
    OS.UpdateWindow(shell.handle);
    OS.SetActiveWindow(shell.handle);
  }
Masker answered 14/5, 2014 at 11:25 Comment(1)
Unfortunately, this workaround doesn't work on my copy of Windows 10.Liminal
C
2
private static void onTop(Shell shell) {
        int s = -1;
        Shell[] shells = display.getShells();
        for (int i = 0; i < shells.length; ++i) {
            if (!shells[i].equals(shell)) {
                shells[i].setEnabled(false);
                shells[i].update();
            } else {
                s = i;
            }
        }
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        for (int i = 0; i < shells.length; ++i) {
            if (i != s) {
                shells[i].setEnabled(true);
                shells[i].update();
            }
        }
    }
Chaperone answered 7/10, 2011 at 19:30 Comment(0)
G
1

There is a way to make it work with what you initially tried. You actually need to call shell.setMinimized(false) and after that shell.setActive() to restore the previous state of the shell. However, that only works if the shell was truely in the minimized state. So here is my final solution, which artificially minimizes the shell if it was not minimized already. The cost is a quick animation if the minimization has to be done.

shell.getDisplay().syncExec(new Runnable() {

    @Override
    public void run() {
        if (!shell.getMinimized())
        {
            shell.setMinimized(true);
        }
        shell.setMinimized(false);
        shell.setActive();
    }
});
Goldfarb answered 25/8, 2015 at 10:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.