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:
parent
db11b9b13b
commit
27bd47a333
9 changed files with 73 additions and 13 deletions
|
@ -548,11 +548,9 @@ std::vector<int> TopLevelWindow::GetPosition() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
|
||||||
void TopLevelWindow::MoveTop() {
|
void TopLevelWindow::MoveTop() {
|
||||||
window_->MoveTop();
|
window_->MoveTop();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void TopLevelWindow::SetTitle(const std::string& title) {
|
void TopLevelWindow::SetTitle(const std::string& title) {
|
||||||
window_->SetTitle(title);
|
window_->SetTitle(title);
|
||||||
|
@ -1067,9 +1065,7 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("setResizable", &TopLevelWindow::SetResizable)
|
.SetMethod("setResizable", &TopLevelWindow::SetResizable)
|
||||||
.SetMethod("isResizable", &TopLevelWindow::IsResizable)
|
.SetMethod("isResizable", &TopLevelWindow::IsResizable)
|
||||||
.SetMethod("setMovable", &TopLevelWindow::SetMovable)
|
.SetMethod("setMovable", &TopLevelWindow::SetMovable)
|
||||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
|
||||||
.SetMethod("moveTop", &TopLevelWindow::MoveTop)
|
.SetMethod("moveTop", &TopLevelWindow::MoveTop)
|
||||||
#endif
|
|
||||||
.SetMethod("isMovable", &TopLevelWindow::IsMovable)
|
.SetMethod("isMovable", &TopLevelWindow::IsMovable)
|
||||||
.SetMethod("setMinimizable", &TopLevelWindow::SetMinimizable)
|
.SetMethod("setMinimizable", &TopLevelWindow::SetMinimizable)
|
||||||
.SetMethod("isMinimizable", &TopLevelWindow::IsMinimizable)
|
.SetMethod("isMinimizable", &TopLevelWindow::IsMinimizable)
|
||||||
|
|
|
@ -126,9 +126,7 @@ class TopLevelWindow : public mate::TrackableObject<TopLevelWindow>,
|
||||||
void SetResizable(bool resizable);
|
void SetResizable(bool resizable);
|
||||||
bool IsResizable();
|
bool IsResizable();
|
||||||
void SetMovable(bool movable);
|
void SetMovable(bool movable);
|
||||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
|
||||||
void MoveTop();
|
void MoveTop();
|
||||||
#endif
|
|
||||||
bool IsMovable();
|
bool IsMovable();
|
||||||
void SetMinimizable(bool minimizable);
|
void SetMinimizable(bool minimizable);
|
||||||
bool IsMinimizable();
|
bool IsMinimizable();
|
||||||
|
|
|
@ -113,9 +113,7 @@ class NativeWindow : public base::SupportsUserData,
|
||||||
virtual double GetSheetOffsetX();
|
virtual double GetSheetOffsetX();
|
||||||
virtual double GetSheetOffsetY();
|
virtual double GetSheetOffsetY();
|
||||||
virtual void SetResizable(bool resizable) = 0;
|
virtual void SetResizable(bool resizable) = 0;
|
||||||
#if defined(OS_WIN) || defined(OS_MACOSX)
|
|
||||||
virtual void MoveTop() = 0;
|
virtual void MoveTop() = 0;
|
||||||
#endif
|
|
||||||
virtual bool IsResizable() = 0;
|
virtual bool IsResizable() = 0;
|
||||||
virtual void SetMovable(bool movable) = 0;
|
virtual void SetMovable(bool movable) = 0;
|
||||||
virtual bool IsMovable() = 0;
|
virtual bool IsMovable() = 0;
|
||||||
|
|
|
@ -657,15 +657,19 @@ void NativeWindowViews::SetResizable(bool resizable) {
|
||||||
resizable_ = resizable;
|
resizable_ = resizable;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_WIN)
|
|
||||||
void NativeWindowViews::MoveTop() {
|
void NativeWindowViews::MoveTop() {
|
||||||
|
// TODO(julien.isorce): fix chromium in order to use existing
|
||||||
|
// widget()->StackAtTop().
|
||||||
|
#if defined(OS_WIN)
|
||||||
gfx::Point pos = GetPosition();
|
gfx::Point pos = GetPosition();
|
||||||
gfx::Size size = GetSize();
|
gfx::Size size = GetSize();
|
||||||
::SetWindowPos(GetAcceleratedWidget(), HWND_TOP, pos.x(), pos.y(),
|
::SetWindowPos(GetAcceleratedWidget(), HWND_TOP, pos.x(), pos.y(),
|
||||||
size.width(), size.height(),
|
size.width(), size.height(),
|
||||||
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
|
||||||
}
|
#elif defined(USE_X11)
|
||||||
|
atom::MoveWindowToForeground(GetAcceleratedWidget());
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool NativeWindowViews::IsResizable() {
|
bool NativeWindowViews::IsResizable() {
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
|
|
|
@ -74,9 +74,7 @@ class NativeWindowViews : public NativeWindow,
|
||||||
void SetContentSizeConstraints(
|
void SetContentSizeConstraints(
|
||||||
const extensions::SizeConstraints& size_constraints) override;
|
const extensions::SizeConstraints& size_constraints) override;
|
||||||
void SetResizable(bool resizable) override;
|
void SetResizable(bool resizable) override;
|
||||||
#if defined(OS_WIN)
|
|
||||||
void MoveTop() override;
|
void MoveTop() override;
|
||||||
#endif
|
|
||||||
bool IsResizable() override;
|
bool IsResizable() override;
|
||||||
void SetMovable(bool movable) override;
|
void SetMovable(bool movable) override;
|
||||||
bool IsMovable() override;
|
bool IsMovable() override;
|
||||||
|
|
|
@ -88,4 +88,25 @@ bool ShouldUseGlobalMenuBar() {
|
||||||
return false;
|
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
|
} // namespace atom
|
||||||
|
|
|
@ -23,6 +23,9 @@ void SetWindowType(::Window xwindow, const std::string& type);
|
||||||
// Returns true if the bus name "com.canonical.AppMenu.Registrar" is available.
|
// Returns true if the bus name "com.canonical.AppMenu.Registrar" is available.
|
||||||
bool ShouldUseGlobalMenuBar();
|
bool ShouldUseGlobalMenuBar();
|
||||||
|
|
||||||
|
// Bring the given window to the front and give it the focus.
|
||||||
|
void MoveWindowToForeground(::Window xwindow);
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
||||||
#endif // ATOM_BROWSER_UI_X_X_WINDOW_UTILS_H_
|
#endif // ATOM_BROWSER_UI_X_X_WINDOW_UTILS_H_
|
||||||
|
|
|
@ -1089,7 +1089,7 @@ can not be focused on.
|
||||||
|
|
||||||
Returns `Boolean` - Whether the window is always on top of other windows.
|
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
|
Moves window to top(z-order) regardless of focus
|
||||||
|
|
||||||
|
|
|
@ -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) => {
|
describe('BrowserWindow.capturePage(rect)', (done) => {
|
||||||
it('returns a Promise with a Buffer', async () => {
|
it('returns a Promise with a Buffer', async () => {
|
||||||
const image = await w.capturePage({
|
const image = await w.capturePage({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue