diff --git a/internal_packages/composer/stylesheets/composer.less b/internal_packages/composer/stylesheets/composer.less index 087e87c5c..955774491 100644 --- a/internal_packages/composer/stylesheets/composer.less +++ b/internal_packages/composer/stylesheets/composer.less @@ -133,7 +133,6 @@ body.platform-win32 { z-index: 2; cursor: default; padding-right: @spacing-standard + @spacing-half; - padding-left: @spacing-standard; padding-top: 12px; .action { @@ -141,6 +140,9 @@ body.platform-win32 { img.content-mask { background-color: @text-color-very-subtle; } font-size: @font-size-small; padding: 10px 6px; + &:first-child { + padding-left: @spacing-standard; + } &:last-child { padding-right: 0; } diff --git a/src/components/key-commands-region.cjsx b/src/components/key-commands-region.cjsx index a3224cee7..925f1fd0b 100644 --- a/src/components/key-commands-region.cjsx +++ b/src/components/key-commands-region.cjsx @@ -104,37 +104,6 @@ class KeyCommandsRegion extends React.Component @state = focused: false - @_in = (event) => - @_lastFocusElement = event.target - @_losingFocusToElement = null - @props.onFocusIn(event) if @state.focused is false - @setState(focused: true) - - @_processOutDebounced = _.debounce => - return unless @_losingFocusToElement - return unless @state.focused - - # This happens when component that used to have the focus is - # unmounted. An example is the url input field of the - # FloatingToolbar in the Composer's Contenteditable - return if ReactDOM.findDOMNode(@).contains(document.activeElement) - - # This prevents the strange effect of an input appearing to have focus - # when the element receiving focus does not support selection (like a - # div with tabIndex=-1) - if @_losingFocusToElement.tagName isnt 'INPUT' - document.getSelection().empty() - - @props.onFocusOut(@_lastFocusElement) - @setState({focused: false}) - @_losingFocusToElement = null - , 150 - - @_out = (event) => - @_lastFocusElement = event.target - @_losingFocusToElement = event.relatedTarget - @_processOutDebounced() - componentWillReceiveProps: (newProps) -> @_unmountListeners() @_setupListeners(newProps) @@ -166,8 +135,8 @@ class KeyCommandsRegion extends React.Component # here for all commands coming in. _setupListeners: (props) -> $el = ReactDOM.findDOMNode(@) - $el.addEventListener('focusin', @_in) - $el.addEventListener('focusout', @_out) + $el.addEventListener('focusin', @_onFocusIn) + $el.addEventListener('focusout', @_onFocusOut) if props.globalHandlers @_globalDisposable = NylasEnv.commands.add(document.body, props.globalHandlers) @@ -182,14 +151,61 @@ class KeyCommandsRegion extends React.Component @_localDisposable?.dispose() @_localDisposable = null $el = ReactDOM.findDOMNode(@) - $el.removeEventListener('focusin', @_in) - $el.removeEventListener('focusout', @_out) + $el.removeEventListener('focusin', @_onFocusIn) + $el.removeEventListener('focusout', @_onDidFocusOut) window.removeEventListener('browser-window-blur', @_onWindowBlur) @_goingout = false _onWindowBlur: => @setState(focused: false) + _onFocusIn: (event) => + @_lastFocusElement = event.target + @_losingFocusToElement = null + @props.onFocusIn(event) if @state.focused is false + @setState(focused: true) + + _onFocusOut: (event) => + @_lastFocusElement = event.target + @_losingFocusToElement = event.relatedTarget + + # Focus could be lost for a moment and programatically restored. To support + # old machines with slow CPUs, it's important we wait N frames rather than X + # msec to see if focus is restored before declaring it "out" for good. + attempt = => + if not @_losingFocusToElement + @_losingFocusFrames = 0 + return + + @_losingFocusFrames -= 1 + if @_losingFocusFrames is 0 + @_onDefinitelyFocusedOut() + else + window.requestAnimationFrame(attempt) + + if !@_losingFocusFrames + window.requestAnimationFrame(attempt) + @_losingFocusFrames = 10 # at 60fps, roughly 150ms + + _onDefinitelyFocusedOut: => + return unless @_losingFocusToElement + return unless @state.focused + + # This happens when component that used to have the focus is + # unmounted. An example is the url input field of the + # FloatingToolbar in the Composer's Contenteditable + return if ReactDOM.findDOMNode(@).contains(document.activeElement) + + # This prevents the strange effect of an input appearing to have focus + # when the element receiving focus does not support selection (like a + # div with tabIndex=-1) + if @_losingFocusToElement.tagName isnt 'INPUT' + document.getSelection().empty() + + @props.onFocusOut(@_lastFocusElement) + @setState({focused: false}) + @_losingFocusToElement = null + render: -> classname = classNames 'key-commands-region': true