mirror of
https://github.com/atom/atom.git
synced 2026-01-23 05:48:10 -05:00
260 lines
8.6 KiB
JavaScript
260 lines
8.6 KiB
JavaScript
const {Disposable, CompositeDisposable} = require('event-kit')
|
|
const listen = require('./delegated-listener')
|
|
|
|
// Handles low-level events related to the `window`.
|
|
module.exports =
|
|
class WindowEventHandler {
|
|
constructor ({atomEnvironment, applicationDelegate}) {
|
|
this.handleDocumentKeyEvent = this.handleDocumentKeyEvent.bind(this)
|
|
this.handleFocusNext = this.handleFocusNext.bind(this)
|
|
this.handleFocusPrevious = this.handleFocusPrevious.bind(this)
|
|
this.handleWindowBlur = this.handleWindowBlur.bind(this)
|
|
this.handleWindowResize = this.handleWindowResize.bind(this)
|
|
this.handleEnterFullScreen = this.handleEnterFullScreen.bind(this)
|
|
this.handleLeaveFullScreen = this.handleLeaveFullScreen.bind(this)
|
|
this.handleWindowBeforeunload = this.handleWindowBeforeunload.bind(this)
|
|
this.handleWindowToggleFullScreen = this.handleWindowToggleFullScreen.bind(this)
|
|
this.handleWindowClose = this.handleWindowClose.bind(this)
|
|
this.handleWindowReload = this.handleWindowReload.bind(this)
|
|
this.handleWindowToggleDevTools = this.handleWindowToggleDevTools.bind(this)
|
|
this.handleWindowToggleMenuBar = this.handleWindowToggleMenuBar.bind(this)
|
|
this.handleLinkClick = this.handleLinkClick.bind(this)
|
|
this.handleDocumentContextmenu = this.handleDocumentContextmenu.bind(this)
|
|
this.atomEnvironment = atomEnvironment
|
|
this.applicationDelegate = applicationDelegate
|
|
this.reloadRequested = false
|
|
this.subscriptions = new CompositeDisposable()
|
|
|
|
this.handleNativeKeybindings()
|
|
}
|
|
|
|
initialize (window, document) {
|
|
this.window = window
|
|
this.document = document
|
|
this.subscriptions.add(this.atomEnvironment.commands.add(this.window, {
|
|
'window:toggle-full-screen': this.handleWindowToggleFullScreen,
|
|
'window:close': this.handleWindowClose,
|
|
'window:reload': this.handleWindowReload,
|
|
'window:toggle-dev-tools': this.handleWindowToggleDevTools
|
|
}))
|
|
|
|
if (['win32', 'linux'].includes(process.platform)) {
|
|
this.subscriptions.add(this.atomEnvironment.commands.add(this.window,
|
|
{'window:toggle-menu-bar': this.handleWindowToggleMenuBar})
|
|
)
|
|
}
|
|
|
|
this.subscriptions.add(this.atomEnvironment.commands.add(this.document, {
|
|
'core:focus-next': this.handleFocusNext,
|
|
'core:focus-previous': this.handleFocusPrevious
|
|
}))
|
|
|
|
this.addEventListener(this.window, 'beforeunload', this.handleWindowBeforeunload)
|
|
this.addEventListener(this.window, 'focus', this.handleWindowFocus)
|
|
this.addEventListener(this.window, 'blur', this.handleWindowBlur)
|
|
this.addEventListener(this.window, 'resize', this.handleWindowResize)
|
|
|
|
this.addEventListener(this.document, 'keyup', this.handleDocumentKeyEvent)
|
|
this.addEventListener(this.document, 'keydown', this.handleDocumentKeyEvent)
|
|
this.addEventListener(this.document, 'drop', this.handleDocumentDrop)
|
|
this.addEventListener(this.document, 'dragover', this.handleDocumentDragover)
|
|
this.addEventListener(this.document, 'contextmenu', this.handleDocumentContextmenu)
|
|
this.subscriptions.add(listen(this.document, 'click', 'a', this.handleLinkClick))
|
|
this.subscriptions.add(listen(this.document, 'submit', 'form', this.handleFormSubmit))
|
|
|
|
this.subscriptions.add(this.applicationDelegate.onDidEnterFullScreen(this.handleEnterFullScreen))
|
|
this.subscriptions.add(this.applicationDelegate.onDidLeaveFullScreen(this.handleLeaveFullScreen))
|
|
}
|
|
|
|
// Wire commands that should be handled by Chromium for elements with the
|
|
// `.native-key-bindings` class.
|
|
handleNativeKeybindings () {
|
|
const bindCommandToAction = (command, action) => {
|
|
this.subscriptions.add(
|
|
this.atomEnvironment.commands.add(
|
|
'.native-key-bindings',
|
|
command,
|
|
event => this.applicationDelegate.getCurrentWindow().webContents[action](),
|
|
false
|
|
)
|
|
)
|
|
}
|
|
|
|
bindCommandToAction('core:copy', 'copy')
|
|
bindCommandToAction('core:paste', 'paste')
|
|
bindCommandToAction('core:undo', 'undo')
|
|
bindCommandToAction('core:redo', 'redo')
|
|
bindCommandToAction('core:select-all', 'selectAll')
|
|
bindCommandToAction('core:cut', 'cut')
|
|
}
|
|
|
|
unsubscribe () {
|
|
this.subscriptions.dispose()
|
|
}
|
|
|
|
on (target, eventName, handler) {
|
|
target.on(eventName, handler)
|
|
this.subscriptions.add(new Disposable(function () {
|
|
target.removeListener(eventName, handler)
|
|
}))
|
|
}
|
|
|
|
addEventListener (target, eventName, handler) {
|
|
target.addEventListener(eventName, handler)
|
|
this.subscriptions.add(new Disposable(function () {
|
|
target.removeEventListener(eventName, handler)
|
|
}))
|
|
}
|
|
|
|
handleDocumentKeyEvent (event) {
|
|
this.atomEnvironment.keymaps.handleKeyboardEvent(event)
|
|
event.stopImmediatePropagation()
|
|
}
|
|
|
|
handleDrop (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
}
|
|
|
|
handleDragover (event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
event.dataTransfer.dropEffect = 'none'
|
|
}
|
|
|
|
eachTabIndexedElement (callback) {
|
|
for (let element of this.document.querySelectorAll('[tabindex]')) {
|
|
if (element.disabled) { continue }
|
|
if (!(element.tabIndex >= 0)) { continue }
|
|
callback(element, element.tabIndex)
|
|
}
|
|
}
|
|
|
|
handleFocusNext () {
|
|
const focusedTabIndex = this.document.activeElement.tabIndex != null ? this.document.activeElement.tabIndex : -Infinity
|
|
|
|
let nextElement = null
|
|
let nextTabIndex = Infinity
|
|
let lowestElement = null
|
|
let lowestTabIndex = Infinity
|
|
this.eachTabIndexedElement(function (element, tabIndex) {
|
|
if (tabIndex < lowestTabIndex) {
|
|
lowestTabIndex = tabIndex
|
|
lowestElement = element
|
|
}
|
|
|
|
if (focusedTabIndex < tabIndex && tabIndex < nextTabIndex) {
|
|
nextTabIndex = tabIndex
|
|
nextElement = element
|
|
}
|
|
})
|
|
|
|
if (nextElement != null) {
|
|
nextElement.focus()
|
|
} else if (lowestElement != null) {
|
|
lowestElement.focus()
|
|
}
|
|
}
|
|
|
|
handleFocusPrevious () {
|
|
const focusedTabIndex = this.document.activeElement.tabIndex != null ? this.document.activeElement.tabIndex : Infinity
|
|
|
|
let previousElement = null
|
|
let previousTabIndex = -Infinity
|
|
let highestElement = null
|
|
let highestTabIndex = -Infinity
|
|
this.eachTabIndexedElement(function (element, tabIndex) {
|
|
if (tabIndex > highestTabIndex) {
|
|
highestTabIndex = tabIndex
|
|
highestElement = element
|
|
}
|
|
|
|
if (focusedTabIndex > tabIndex && tabIndex > previousTabIndex) {
|
|
previousTabIndex = tabIndex
|
|
previousElement = element
|
|
}
|
|
})
|
|
|
|
if (previousElement != null) {
|
|
previousElement.focus()
|
|
} else if (highestElement != null) {
|
|
highestElement.focus()
|
|
}
|
|
}
|
|
|
|
handleWindowFocus () {
|
|
this.document.body.classList.remove('is-blurred')
|
|
}
|
|
|
|
handleWindowBlur () {
|
|
this.document.body.classList.add('is-blurred')
|
|
this.atomEnvironment.storeWindowDimensions()
|
|
}
|
|
|
|
handleWindowResize () {
|
|
this.atomEnvironment.storeWindowDimensions()
|
|
}
|
|
|
|
handleEnterFullScreen () {
|
|
this.document.body.classList.add('fullscreen')
|
|
}
|
|
|
|
handleLeaveFullScreen () {
|
|
this.document.body.classList.remove('fullscreen')
|
|
}
|
|
|
|
handleWindowBeforeunload (event) {
|
|
if (!this.reloadRequested && !this.atomEnvironment.inSpecMode() && this.atomEnvironment.getCurrentWindow().isWebViewFocused()) {
|
|
this.atomEnvironment.hide()
|
|
}
|
|
this.reloadRequested = false
|
|
this.atomEnvironment.storeWindowDimensions()
|
|
this.atomEnvironment.unloadEditorWindow()
|
|
this.atomEnvironment.destroy()
|
|
}
|
|
|
|
handleWindowToggleFullScreen () {
|
|
this.atomEnvironment.toggleFullScreen()
|
|
}
|
|
|
|
handleWindowClose () {
|
|
this.atomEnvironment.close()
|
|
}
|
|
|
|
handleWindowReload () {
|
|
this.reloadRequested = true
|
|
this.atomEnvironment.reload()
|
|
}
|
|
|
|
handleWindowToggleDevTools () {
|
|
this.atomEnvironment.toggleDevTools()
|
|
}
|
|
|
|
handleWindowToggleMenuBar () {
|
|
this.atomEnvironment.config.set('core.autoHideMenuBar', !this.atomEnvironment.config.get('core.autoHideMenuBar'))
|
|
|
|
if (this.atomEnvironment.config.get('core.autoHideMenuBar')) {
|
|
const detail = 'To toggle, press the Alt key or execute the window:toggle-menu-bar command'
|
|
this.atomEnvironment.notifications.addInfo('Menu bar hidden', {detail})
|
|
}
|
|
}
|
|
|
|
handleLinkClick (event) {
|
|
event.preventDefault()
|
|
const uri = event.currentTarget && event.currentTarget.getAttribute('href')
|
|
if (uri && (uri[0] !== '#') && /^https?:\/\//.test(uri)) {
|
|
this.applicationDelegate.openExternal(uri)
|
|
}
|
|
}
|
|
|
|
handleFormSubmit (event) {
|
|
// Prevent form submits from changing the current window's URL
|
|
event.preventDefault()
|
|
}
|
|
|
|
handleDocumentContextmenu (event) {
|
|
event.preventDefault()
|
|
this.atomEnvironment.contextMenu.showForEvent(event)
|
|
}
|
|
}
|