Skip to content
This repository was archived by the owner on Dec 11, 2019. It is now read-only.

implement find in page #111

Merged
merged 2 commits into from
Dec 28, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ const init = () => {
type: 'separator'
}, {
label: 'Find on page...',
accelerator: 'CmdOrCtrl+F'
accelerator: 'CmdOrCtrl+F',
click: function (item, focusedWindow) {
sendToFocusedWindow(focusedWindow, [messages.SHORTCUT_ACTIVE_FRAME_SHOW_FINDBAR])
}
}, {
label: 'Find Next',
accelerator: 'CmdOrCtrl+G'
Expand Down
7 changes: 6 additions & 1 deletion docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ WindowStore
location: string, // page url
src: string, // what the iframe src should be
title: string, // page title
findbarShown: boolean, // whether the findbar is shown
thumbnail: string, // url to thumbnail
key: number,
isPrivate: boolean, // private browsing tab
Expand All @@ -58,7 +59,11 @@ WindowStore
parentFrameKey: number, // the key of the frame this frame was opened from
contextMenuDetail: {...},
modalPromptDetail: {...},
basicAuthDetail: {...}
basicAuthDetail: {...},
findDetail: {
searchString: string, // the string being searched
caseSensitivity: boolean // whether we are doing a case sensitive search
}
unloaded: boolean, // true if the tab is unloaded
navbar: {
focused: boolean, // whether the navbar is focused
Expand Down
26 changes: 26 additions & 0 deletions js/actions/windowActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ const WindowActions = {
})
},

/**
* Shows/hides the find-in-page bar.
* @param {Object} frameProps - The frame properties to modify
* @param {boolean} shown - Whether to show the findbar
*/
setFindbarShown: function (frameProps, shown) {
WindowDispatcher.dispatch({
actionType: WindowConstants.WINDOW_SET_FINDBAR_SHOWN,
frameProps,
shown
})
},

/**
* Dispatches a message to the store to indicate that the webview is loading.
*
Expand Down Expand Up @@ -412,6 +425,19 @@ const WindowActions = {
})
},

/**
* Dispatches a message to set the find-in-page details.
* @param {Object} frameProps - Properties of the frame in question
* @param {Object} findDetail - the find details
*/
setFindDetail: function (frameProps, findDetail) {
WindowDispatcher.dispatch({
actionType: WindowConstants.WINDOW_SET_FIND_DETAIL,
frameProps,
findDetail
})
},

/**
* Dispatches a message to indicate that the frame should be muted
*
Expand Down
157 changes: 157 additions & 0 deletions js/components/findbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

const React = require('react')
const ReactDOM = require('react-dom')
const ImmutableComponent = require('./immutableComponent')
const Immutable = require('immutable')
const keyCodes = require('../constants/keyCodes')
const Button = require('./button.js')
const WindowActions = require('../actions/windowActions')

export default class FindBar extends ImmutableComponent {
constructor () {
super()
}

onChange (e) {
WindowActions.setFindDetail(this.props.frame, Immutable.fromJS({
searchString: e.target.value,
caseSensitivity: this.props.findDetail.get('caseSensitivity')
}))
}

onCaseSensitivityChange (e) {
WindowActions.setFindDetail(this.props.frame, Immutable.fromJS({
searchString: this.props.findDetail.get('searchString'),
caseSensitivity: e.target.checked
}))
}

onFind () {
this.props.onFindAll(this.props.findDetail.get('searchString'),
this.props.findDetail.get('caseSensitivity'))
}

onFindNext () {
this.props.onFindAll(this.props.findDetail.get('searchString'),
this.props.findDetail.get('caseSensitivity'),
true)
}

onFindPrev () {
this.props.onFindAgain(this.props.findDetail.get('searchString'),
this.props.findDetail.get('caseSensitivity'),
false)
}

/**
* Focus the find in page input and select the text
*/
focus () {
let input = ReactDOM.findDOMNode(this.refs.searchString)
input.focus()
input.select()
}

componentDidUpdate (prevProps) {
if (!this.props.active) {
return null
}
if (!prevProps.active) {
// Focus and select the find input
this.focus()
}
if (this.props.findDetail !== prevProps.findDetail) {
// Redo search if details have changed
this.onFind()
}
}

onKeyDown (e) {
switch (e.keyCode) {
case keyCodes.ESC:
// ESC is handled by a local shortcut, so use shift+ESC
if (e.shiftKey) {
this.props.onHide()
}
break
case keyCodes.ENTER:
if (e.shiftKey) {
this.onFindPrev()
} else {
this.onFind()
}
break
}
}

get numberofMatches () {
// TODO: Hook this up when found-in-page event fires
if (!this.props.findInPageDetail) {
return -1
}
return this.props.findInPageDetail.get('numberOfMatches')
}

get activeMatchOrdinal () {
if (!this.props.findInPageDetail) {
return -1
}
return this.props.findInPageDetail.get('activeMatchOrdinal')
}

get isCaseSensitive () {
this.props.findDetail.get('caseSensitivity')
}

render () {
if (!this.props.active) {
return null
}

let findMatchText
if (this.numberofMatches !== -1 && this.props.findDetail.get('searchString')) {
let l10nArgs = {
activeMatchOrdinal: this.activeMatchOrdinal,
numberofMatches: this.numberofMatches
}
findMatchText = <span data-l10n-id='findResults'
data-l10n-args={JSON.stringify(l10nArgs)}>{JSON.stringify(l10nArgs)}</span>
}

return <div className='findBar'>
<span className='searchStringContainer'>
<input type='text'
ref='searchString'
onKeyDown={this.onKeyDown.bind(this)}
onChange={this.onChange.bind(this)}
value={this.props.findDetail.get('searchString')}/>
{findMatchText}
</span>
<Button iconClass='findButton fa-chevron-up'
className='findButton smallButton findPrev'
disabled={this.numberofMatches === 0}
onClick={this.onFindPrev.bind(this)} />
<Button iconClass='findButton fa-chevron-down'
className='findButton smallButton findNext'
disabled={this.numberofMatches === 0}
onClick={this.onFindNext.bind(this)} />
<Button iconClass='fa-times'
className='findButton smallButton hideButton'
onClick={this.props.onHide} />
<div className='caseSensitivityContainer'>
<input
id='caseSensitivityCheckbox'
type='checkbox'
className='caseSensitivityCheckbox'
checked={this.isCaseSensitive}
onChange={this.onCaseSensitivityChange.bind(this)} />
<label htmlFor='caseSensitivityCheckbox' data-l10n-id='caseSensitivity'>
{'Match case'}
</label>
</div>
</div>
}
}
45 changes: 45 additions & 0 deletions js/components/frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const remote = global.require('electron').remote

import adInfo from '../data/adInfo.js'
import Config from '../constants/config.js'
import FindBar from './findbar.js'

class Frame extends ImmutableComponent {
constructor () {
Expand Down Expand Up @@ -73,6 +74,9 @@ class Frame extends ImmutableComponent {
case 'print':
this.webview.send(messages.PRINT_PAGE)
break
case 'show-findbar':
WindowActions.setFindbarShown(this.props.frame, true)
break
}
if (activeShortcut) {
WindowActions.setActiveFrameShortcut(null)
Expand Down Expand Up @@ -184,6 +188,37 @@ class Frame extends ImmutableComponent {
WindowActions.setTabPageIndexByFrame(this.props.frame)
}

onFindHide () {
WindowActions.setFindbarShown(this.props.frame, false)
this.onClearMatch()
}

onFindAll (searchString, caseSensitivity) {
if (searchString) {
this.webview.findInPage(searchString,
{matchCase: caseSensitivity,
forward: true,
findNext: false})
} else {
this.onClearMatch()
}
}

onFindAgain (searchString, caseSensitivity, forward) {
if (searchString) {
this.webview.findInPage(searchString,
{matchCase: caseSensitivity,
forward: forward,
findNext: true})
} else {
this.onClearMatch()
}
}

onClearMatch () {
this.webview.stopFindInPage('clearSelection')
}

componentWillReceiveProps (nextProps) {
if (nextProps.frame.get('audioMuted') &&
this.props.frame.get('audioMuted') !== true) {
Expand All @@ -200,6 +235,16 @@ class Frame extends ImmutableComponent {
frameWrapper: true,
isActive: this.props.isActive
})}>
<FindBar
ref='findbar'
findInPageDetail={null}
onFindAll={this.onFindAll.bind(this)}
onFindAgain={this.onFindAgain.bind(this)}
onHide={this.onFindHide.bind(this)}
active={this.props.frame.get('findbarShown')}
frame={this.props.frame}
findDetail={this.props.frame.get('findDetail')}
/>
<webview
ref='webview'
onFocus={this.onFocus.bind(this)}
Expand Down
1 change: 1 addition & 0 deletions js/constants/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const messages = {
SHORTCUT_SET_ACTIVE_FRAME_TO_LAST: _,
SHORTCUT_ACTIVE_FRAME_SAVE: _,
SHORTCUT_ACTIVE_FRAME_PRINT: _,
SHORTCUT_ACTIVE_FRAME_SHOW_FINDBAR: _,
// Frame management shortcuts
SHORTCUT_NEW_FRAME: _, /** @arg {string} opt_url to load if any */
SHORTCUT_CLOSE_FRAME: _, /** @arg {number} opt_key of frame, defaults to active frame */
Expand Down
4 changes: 3 additions & 1 deletion js/constants/windowConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ const windowConstants = {
WINDOW_SET_ACTIVE_FRAME_SHORTCUT: _,
WINDOW_SET_URL_BAR_SELECTED: _,
WINDOW_SET_SEARCH_DETAIL: _,
WINDOW_SET_FIND_DETAIL: _,
WINDOW_SET_AUDIO_MUTED: _,
WINDOW_SET_AUDIO_PLAYBACK_ACTIVE: _,
WINDOW_SET_FAVICON: _,
WINDOW_SET_MOUSE_IN_TITLEBAR: _
WINDOW_SET_MOUSE_IN_TITLEBAR: _,
WINDOW_SET_FINDBAR_SHOWN: _ // whether the findbar is shown
}

module.exports = mapValuesByKeys(windowConstants)
1 change: 1 addition & 0 deletions js/entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require('../less/button.less')
require('../less/main.less')
require('../less/navigationBar.less')
require('../less/tabs.less')
require('../less/findbar.less')
require('../node_modules/font-awesome/css/font-awesome.css')

const URL = require('url')
Expand Down
6 changes: 5 additions & 1 deletion js/state/frameStateUtil.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ export function addFrame (frames, frameOpts, newKey, activeFrameKey) {
active: false
}
},
searchDetail: null
searchDetail: null,
findDetail: {
searchString: '',
caseSensitivity: false
}
})

// Find the closest index to the current frame's index which has
Expand Down
12 changes: 11 additions & 1 deletion js/stores/windowStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ WindowDispatcher.register((action) => {
})
windowStore.emitChange()
break
case WindowConstants.WINDOW_SET_FINDBAR_SHOWN:
windowState = windowState.mergeIn(['frames', FrameStateUtil.getFramePropsIndex(windowState.get('frames'), action.frameProps)], {
findbarShown: action.shown
})
windowStore.emitChange()
break
case WindowConstants.WINDOW_WEBVIEW_LOAD_START:
windowState = windowState.mergeIn(['frames', FrameStateUtil.getFramePropsIndex(windowState.get('frames'), action.frameProps)], {
loading: true,
Expand Down Expand Up @@ -302,6 +308,10 @@ WindowDispatcher.register((action) => {
})
windowStore.emitChange()
break
case WindowConstants.WINDOW_SET_FIND_DETAIL:
windowState = windowState.setIn(['frames', FrameStateUtil.getFramePropsIndex(windowState.get('frames'), action.frameProps), 'findDetail'], action.findDetail)
windowStore.emitChange()
break
case WindowConstants.WINDOW_SET_AUDIO_MUTED:
windowState = windowState.setIn(['frames', FrameStateUtil.getFramePropsIndex(windowState.get('frames'), action.frameProps), 'audioMuted'], action.muted)
windowStore.emitChange()
Expand Down Expand Up @@ -334,7 +344,7 @@ ipc.on(messages.SHORTCUT_PREV_TAB, () => {
windowStore.emitChange()
})

const frameShortcuts = ['stop', 'reload', 'zoom-in', 'zoom-out', 'zoom-reset', 'toggle-dev-tools', 'clean-reload', 'view-source', 'mute', 'save', 'print']
const frameShortcuts = ['stop', 'reload', 'zoom-in', 'zoom-out', 'zoom-reset', 'toggle-dev-tools', 'clean-reload', 'view-source', 'mute', 'save', 'print', 'show-findbar']
frameShortcuts.forEach(shortcut => {
// Listen for actions on the active frame
ipc.on(`shortcut-active-frame-${shortcut}`, () => {
Expand Down
Loading