feat: implement BrowserWindow.moveTop on X11 (#16629)

It was implemented on Mac and Win but not on X11.
Tested on Ubuntu 16.04 and 18.04.

Also added a unit test in spec/api-browser-window-spec.js.
This test BrowserWindow.moveTop verifies that calling moveTop
on a window does not give the focus to this window.

notes: BrowserWindow.moveTop is now available on Linux/x11

https://github.com/electron/electron/issues/12516
This commit is contained in:
Julien Isorce 2019-02-07 12:48:19 -08:00 committed by Shelley Vohr
parent db11b9b13b
commit 27bd47a333
9 changed files with 73 additions and 13 deletions

View file

@ -548,11 +548,9 @@ std::vector<int> TopLevelWindow::GetPosition() {
return result;
}
#if defined(OS_WIN) || defined(OS_MACOSX)
void TopLevelWindow::MoveTop() {
window_->MoveTop();
}
#endif
void TopLevelWindow::SetTitle(const std::string& title) {
window_->SetTitle(title);
@ -1067,9 +1065,7 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate,
.SetMethod("setResizable", &TopLevelWindow::SetResizable)
.SetMethod("isResizable", &TopLevelWindow::IsResizable)
.SetMethod("setMovable", &TopLevelWindow::SetMovable)
#if defined(OS_WIN) || defined(OS_MACOSX)
.SetMethod("moveTop", &TopLevelWindow::MoveTop)
#endif
.SetMethod("isMovable", &TopLevelWindow::IsMovable)
.SetMethod("setMinimizable", &TopLevelWindow::SetMinimizable)
.SetMethod("isMinimizable", &TopLevelWindow::IsMinimizable)

View file

@ -126,9 +126,7 @@ class TopLevelWindow : public mate::TrackableObject<TopLevelWindow>,
void SetResizable(bool resizable);
bool IsResizable();
void SetMovable(bool movable);
#if defined(OS_WIN) || defined(OS_MACOSX)
void MoveTop();
#endif
bool IsMovable();
void SetMinimizable(bool minimizable);
bool IsMinimizable();

View file

@ -113,9 +113,7 @@ class NativeWindow : public base::SupportsUserData,
virtual double GetSheetOffsetX();
virtual double GetSheetOffsetY();
virtual void SetResizable(bool resizable) = 0;
#if defined(OS_WIN) || defined(OS_MACOSX)
virtual void MoveTop() = 0;
#endif
virtual bool IsResizable() = 0;
virtual void SetMovable(bool movable) = 0;
virtual bool IsMovable() = 0;

View file

@ -657,15 +657,19 @@ void NativeWindowViews::SetResizable(bool resizable) {
resizable_ = resizable;
}
#if defined(OS_WIN)
void NativeWindowViews::MoveTop() {
// TODO(julien.isorce): fix chromium in order to use existing
// widget()->StackAtTop().
#if defined(OS_WIN)
gfx::Point pos = GetPosition();
gfx::Size size = GetSize();
::SetWindowPos(GetAcceleratedWidget(), HWND_TOP, pos.x(), pos.y(),
size.width(), size.height(),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
}
#elif defined(USE_X11)
atom::MoveWindowToForeground(GetAcceleratedWidget());
#endif
}
bool NativeWindowViews::IsResizable() {
#if defined(OS_WIN)

View file

@ -74,9 +74,7 @@ class NativeWindowViews : public NativeWindow,
void SetContentSizeConstraints(
const extensions::SizeConstraints& size_constraints) override;
void SetResizable(bool resizable) override;
#if defined(OS_WIN)
void MoveTop() override;
#endif
bool IsResizable() override;
void SetMovable(bool movable) override;
bool IsMovable() override;

View file

@ -88,4 +88,25 @@ bool ShouldUseGlobalMenuBar() {
return false;
}
void MoveWindowToForeground(::Window xwindow) {
XDisplay* xdisplay = gfx::GetXDisplay();
XEvent xclient;
memset(&xclient, 0, sizeof(xclient));
xclient.type = ClientMessage;
xclient.xclient.display = xdisplay;
xclient.xclient.window = xwindow;
xclient.xclient.message_type = GetAtom("_NET_RESTACK_WINDOW");
xclient.xclient.format = 32;
xclient.xclient.data.l[0] = 2;
xclient.xclient.data.l[1] = 0;
xclient.xclient.data.l[2] = Above;
xclient.xclient.data.l[3] = 0;
xclient.xclient.data.l[4] = 0;
XSendEvent(xdisplay, DefaultRootWindow(xdisplay), x11::False,
SubstructureRedirectMask | SubstructureNotifyMask, &xclient);
XFlush(xdisplay);
}
} // namespace atom

View file

@ -23,6 +23,9 @@ void SetWindowType(::Window xwindow, const std::string& type);
// Returns true if the bus name "com.canonical.AppMenu.Registrar" is available.
bool ShouldUseGlobalMenuBar();
// Bring the given window to the front and give it the focus.
void MoveWindowToForeground(::Window xwindow);
} // namespace atom
#endif // ATOM_BROWSER_UI_X_X_WINDOW_UTILS_H_

View file

@ -1089,7 +1089,7 @@ can not be focused on.
Returns `Boolean` - Whether the window is always on top of other windows.
#### `win.moveTop()` _macOS_ _Windows_
#### `win.moveTop()`
Moves window to top(z-order) regardless of focus

View file

@ -494,6 +494,48 @@ describe('BrowserWindow module', () => {
})
})
describe('BrowserWindow.moveTop()', () => {
it('should not steal focus', async () => {
const posDelta = 50
const wShownInactive = emittedOnce(w, 'show')
w.showInactive()
await wShownInactive
assert(!w.isFocused())
const otherWindow = new BrowserWindow({ show: false, title: 'otherWindow' })
const otherWindowShown = emittedOnce(otherWindow, 'show')
otherWindow.loadURL('data:text/html,<html><body background-color: rgba(255,255,255,0)></body></html>')
otherWindow.show()
await otherWindowShown
assert(otherWindow.isFocused())
w.moveTop()
const wPos = w.getPosition()
const wMoving = emittedOnce(w, 'move')
w.setPosition(wPos[0] + posDelta, wPos[1] + posDelta)
await wMoving
assert(!w.isFocused())
assert(otherWindow.isFocused())
const wFocused = emittedOnce(w, 'focus')
w.focus()
await wFocused
assert(w.isFocused())
otherWindow.moveTop()
const otherWindowPos = otherWindow.getPosition()
const otherWindowMoving = emittedOnce(otherWindow, 'move')
otherWindow.setPosition(otherWindowPos[0] + posDelta, otherWindowPos[1] + posDelta)
await otherWindowMoving
assert(!otherWindow.isFocused())
assert(w.isFocused())
await closeWindow(otherWindow, { assertSingleWindow: false }).then(() => {
assert.strictEqual(BrowserWindow.getAllWindows().length, 2) // Test window + w
})
})
})
describe('BrowserWindow.capturePage(rect)', (done) => {
it('returns a Promise with a Buffer', async () => {
const image = await w.capturePage({