123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- const { Disposable, CompositeDisposable } = require('event-kit');
- const listen = require('./delegated-listener');
- const { debounce } = require('underscore-plus');
- // 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',
- debounce(this.handleWindowResize, 500)
- );
- 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] !== '#') {
- if (/^https?:\/\//.test(uri)) {
- this.applicationDelegate.openExternal(uri);
- } else if (uri.startsWith('atom://')) {
- this.atomEnvironment.uriHandlerRegistry.handleURI(uri);
- }
- }
- }
- handleFormSubmit(event) {
- // Prevent form submits from changing the current window's URL
- event.preventDefault();
- }
- handleDocumentContextmenu(event) {
- event.preventDefault();
- this.atomEnvironment.contextMenu.showForEvent(event);
- }
- };
|