electron-findbar 0.4.0 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/launch.json +1 -1
- package/README.md +63 -33
- package/index.js +109 -104
- package/package.json +9 -4
- package/remote.js +22 -0
- package/test/sample.html +28 -0
- package/test/sample.js +43 -0
- package/web/app.css +16 -10
- package/web/app.js +15 -13
- package/web/findbar.html +1 -1
- package/sample.js +0 -23
- /package/{test.js → test/test.js} +0 -0
package/.vscode/launch.json
CHANGED
package/README.md
CHANGED
|
@@ -47,21 +47,21 @@ All public methods are documented with JSDoc and can be referenced during import
|
|
|
47
47
|
To import the Findbar class:
|
|
48
48
|
|
|
49
49
|
```js
|
|
50
|
-
const { Findbar } = require('electron-findbar')
|
|
50
|
+
const { Findbar } = require('electron-findbar')
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
### Creating the Findbar Instance
|
|
54
54
|
|
|
55
|
-
You can pass a `BrowserWindow` instance as a single parameter to use it as the parent window. The `BrowserWindow.WebContents` will be used as the
|
|
55
|
+
You can pass a `BrowserWindow` instance as a single parameter to use it as the parent window. The `BrowserWindow.WebContents` will be used as the findable content:
|
|
56
56
|
|
|
57
57
|
```js
|
|
58
|
-
const findbar = new Findbar(browserWindow)
|
|
58
|
+
const findbar = new Findbar(browserWindow)
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
Alternatively, you can provide a custom `WebContents` as the second parameter. In this case, the first parameter can be any `BaseWindow`, and the second parameter will be the
|
|
61
|
+
Alternatively, you can provide a custom `WebContents` as the second parameter. In this case, the first parameter can be any `BaseWindow`, and the second parameter will be the findable content:
|
|
62
62
|
|
|
63
63
|
```js
|
|
64
|
-
const findbar = new Findbar(baseWindow, customWebContents)
|
|
64
|
+
const findbar = new Findbar(baseWindow, customWebContents)
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
### Configuring the Findbar
|
|
@@ -69,33 +69,34 @@ const findbar = new Findbar(baseWindow, customWebContents);
|
|
|
69
69
|
You can customize the Findbar window options using the `setWindowOptions` method:
|
|
70
70
|
|
|
71
71
|
```js
|
|
72
|
-
findbar.setWindowOptions({ movable: true, resizable: true, alwaysOnTop: true })
|
|
72
|
+
findbar.setWindowOptions({ movable: true, resizable: true, alwaysOnTop: true })
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
To handle the Findbar window directly after it is opened, use the `setWindowHandler` method:
|
|
76
76
|
|
|
77
77
|
```js
|
|
78
78
|
findbar.setWindowHandler(win => {
|
|
79
|
-
win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
|
|
79
|
+
win.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true })
|
|
80
80
|
});
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
The Findbar is a child window of the `BaseWindow` passed during construction. To open it use:
|
|
83
|
+
The findbar has a default position handler which moves the findbar to the top-right corner. To change the position handler, use the `setPositionHandler` method. The position handler is called when the parent window moves or resizes and provides both the parent and findbar bounds as parameters.
|
|
86
84
|
|
|
87
85
|
```js
|
|
88
|
-
findbar.
|
|
86
|
+
findbar.setPositionHandler((parentBounds, findbarBounds) => ({
|
|
87
|
+
x: parentBounds.x + parentBounds.width - findbarBounds.width - 20,
|
|
88
|
+
y: parentBounds.y - ((findbarBounds.height / 4) | 0)
|
|
89
|
+
}))
|
|
89
90
|
```
|
|
90
91
|
|
|
91
|
-
|
|
92
|
+
### Opening the Findbar
|
|
93
|
+
|
|
94
|
+
The Findbar is a child window of the `BaseWindow` passed during construction. To open it use:
|
|
92
95
|
|
|
93
96
|
```js
|
|
94
|
-
findbar.
|
|
97
|
+
findbar.open()
|
|
95
98
|
```
|
|
96
99
|
|
|
97
|
-
> Enabled by default.
|
|
98
|
-
|
|
99
100
|
### Finding Text in the Page
|
|
100
101
|
|
|
101
102
|
Once open, the Findbar appears by default in the top-right corner of the parent window and can be used without additional coding. Alternatively, you can use the following methods to trigger `findInPage` and navigate through matches in the main process:
|
|
@@ -104,28 +105,28 @@ Once open, the Findbar appears by default in the top-right corner of the parent
|
|
|
104
105
|
/**
|
|
105
106
|
* Retrieve the last queried value.
|
|
106
107
|
*/
|
|
107
|
-
getLastValue()
|
|
108
|
+
getLastValue()
|
|
108
109
|
|
|
109
110
|
/**
|
|
110
111
|
* Initiate a request to find all matches for the specified text on the page.
|
|
111
112
|
* @param {string} text - The text to search for.
|
|
112
113
|
*/
|
|
113
|
-
startFind(text)
|
|
114
|
+
startFind(text)
|
|
114
115
|
|
|
115
116
|
/**
|
|
116
117
|
* Select the previous match, if available.
|
|
117
118
|
*/
|
|
118
|
-
findPrevious()
|
|
119
|
+
findPrevious()
|
|
119
120
|
|
|
120
121
|
/**
|
|
121
122
|
* Select the next match, if available.
|
|
122
123
|
*/
|
|
123
|
-
findNext()
|
|
124
|
+
findNext()
|
|
124
125
|
|
|
125
126
|
/**
|
|
126
127
|
* Stop the find request.
|
|
127
128
|
*/
|
|
128
|
-
stopFind()
|
|
129
|
+
stopFind()
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
### Closing the Findbar
|
|
@@ -133,7 +134,7 @@ stopFind();
|
|
|
133
134
|
When the Findbar is closed, its window is destroyed to free memory resources. Use the following method to close the Findbar:
|
|
134
135
|
|
|
135
136
|
```js
|
|
136
|
-
findbar.close()
|
|
137
|
+
findbar.close()
|
|
137
138
|
```
|
|
138
139
|
|
|
139
140
|
A new internal window will be created the next time the `open` method is called. There is no need to instantiate another Findbar for the same parent window.
|
|
@@ -143,32 +144,61 @@ A new internal window will be created the next time the `open` method is called.
|
|
|
143
144
|
Here is a quick example demonstrating how to use the `electron-findbar`:
|
|
144
145
|
|
|
145
146
|
```js
|
|
146
|
-
const { app, BrowserWindow } = require('electron')
|
|
147
|
-
const { Findbar } = require('electron-findbar')
|
|
147
|
+
const { app, BrowserWindow } = require('electron')
|
|
148
|
+
const { Findbar } = require('electron-findbar')
|
|
148
149
|
|
|
149
150
|
app.whenReady().then(() => {
|
|
150
|
-
const window = new BrowserWindow()
|
|
151
|
-
window.loadURL('https://github.com/ECRomaneli/electron-findbar')
|
|
151
|
+
const window = new BrowserWindow()
|
|
152
|
+
window.loadURL('https://github.com/ECRomaneli/electron-findbar')
|
|
152
153
|
|
|
153
154
|
// Create and configure the Findbar object
|
|
154
|
-
const findbar = new Findbar(window)
|
|
155
|
+
const findbar = new Findbar(window)
|
|
155
156
|
|
|
156
157
|
// [OPTIONAL] Customize window options
|
|
157
|
-
findbar.setWindowOptions({ movable: true, resizable: true })
|
|
158
|
+
findbar.setWindowOptions({ movable: true, resizable: true })
|
|
158
159
|
|
|
159
160
|
// [OPTIONAL] Handle the window object when the Findbar is opened
|
|
160
161
|
findbar.setWindowHandler(win => {
|
|
161
|
-
win.webContents.openDevTools()
|
|
162
|
-
})
|
|
162
|
+
win.webContents.openDevTools()
|
|
163
|
+
})
|
|
163
164
|
|
|
164
165
|
// Open the Findbar
|
|
165
|
-
findbar.open()
|
|
166
|
-
})
|
|
166
|
+
findbar.open()
|
|
167
|
+
})
|
|
167
168
|
```
|
|
168
169
|
|
|
169
|
-
##
|
|
170
|
+
## IPC Events
|
|
171
|
+
|
|
172
|
+
As an alternative, the findbar can be controlled using IPC events in the `renderer` process of the `WebContents` provided during the findbar construction.
|
|
173
|
+
|
|
174
|
+
### ipcRenderer
|
|
170
175
|
|
|
171
|
-
|
|
176
|
+
If the `contextIsolation` is enabled, the `electron-findbar/remote` will not be available, but the IPC events can be used directly through the preload script:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const $remote = (ipc => ({
|
|
180
|
+
getLastText: async () => ipc.invoke('electron-findbar/last-text'),
|
|
181
|
+
inputChange: (value) => { ipc.send('electron-findbar/input-change', value) },
|
|
182
|
+
previous: () => { ipc.send('electron-findbar/previous') },
|
|
183
|
+
next: () => { ipc.send('electron-findbar/next') },
|
|
184
|
+
open: () => { ipc.send('electron-findbar/open') },
|
|
185
|
+
close: () => { ipc.send('electron-findbar/close') },
|
|
186
|
+
})) (require('electron').ipcRenderer)
|
|
187
|
+
|
|
188
|
+
$remote.open()
|
|
189
|
+
$remote.inputChange('findIt')
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Remote module
|
|
193
|
+
|
|
194
|
+
With the `contextIsolation` disabled, the remote library is available to use:
|
|
195
|
+
|
|
196
|
+
```js
|
|
197
|
+
const FindbarRemote = require('electron-findbar/remote')
|
|
198
|
+
|
|
199
|
+
FindbarRemote.open()
|
|
200
|
+
FindbarRemote.inputChange('findIt')
|
|
201
|
+
```
|
|
172
202
|
|
|
173
203
|
## Author
|
|
174
204
|
|
package/index.js
CHANGED
|
@@ -8,7 +8,7 @@ class Findbar {
|
|
|
8
8
|
#window
|
|
9
9
|
|
|
10
10
|
/** @type {WebContents} */
|
|
11
|
-
#
|
|
11
|
+
#findableContents
|
|
12
12
|
|
|
13
13
|
/** */
|
|
14
14
|
#matches
|
|
@@ -16,14 +16,14 @@ class Findbar {
|
|
|
16
16
|
/** @type {(findbarWindow: BrowserWindow) => void} */
|
|
17
17
|
#windowHandler
|
|
18
18
|
|
|
19
|
+
/** @type {{parentBounds: Rectangle, findbarBounds: Rectangle} => {x: number, y: number}} */
|
|
20
|
+
#positionHandler = Findbar.#setDefaultPosition
|
|
21
|
+
|
|
19
22
|
/** @type {BrowserWindowConstructorOptions} */
|
|
20
23
|
#customOptions
|
|
21
24
|
|
|
22
25
|
/** @type {string} */
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
/** @type {boolean} */
|
|
26
|
-
#followParent = process.platform !== 'darwin'
|
|
26
|
+
#lastText = ''
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Workaround to fix "findInPage" bug - double-click to loop
|
|
@@ -39,26 +39,29 @@ class Findbar {
|
|
|
39
39
|
*/
|
|
40
40
|
constructor (parent, webContents) {
|
|
41
41
|
this.#parent = parent
|
|
42
|
-
this.#
|
|
42
|
+
this.#findableContents = webContents ?? parent.webContents
|
|
43
|
+
this.#findableContents._findbar = this
|
|
43
44
|
|
|
44
|
-
if (!this.#
|
|
45
|
+
if (!this.#findableContents) {
|
|
45
46
|
throw new Error('There are no searchable web contents.')
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
* Open the findbar.
|
|
51
|
+
* Open the findbar. If the findbar is already opened, focus the input text.
|
|
51
52
|
*/
|
|
52
53
|
open() {
|
|
53
54
|
if (this.#window) {
|
|
54
|
-
this.#
|
|
55
|
+
this.#focusWindowAndHighlightInput()
|
|
55
56
|
return
|
|
56
57
|
}
|
|
57
58
|
this.#window = new BrowserWindow(Findbar.#mergeStandardOptions(this.#customOptions, this.#parent))
|
|
58
|
-
this.#window.webContents.
|
|
59
|
+
this.#window.webContents._findbar = this
|
|
59
60
|
|
|
60
61
|
this.#registerListeners()
|
|
61
|
-
|
|
62
|
+
|
|
63
|
+
const pos = this.#positionHandler(this.#parent.getBounds(), this.#window.getBounds())
|
|
64
|
+
this.#window.setPosition(pos.x, pos.y)
|
|
62
65
|
|
|
63
66
|
this.#windowHandler && this.#windowHandler(this.#window)
|
|
64
67
|
|
|
@@ -73,19 +76,21 @@ class Findbar {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
/**
|
|
76
|
-
* Get last queried
|
|
79
|
+
* Get last queried text.
|
|
77
80
|
*/
|
|
78
|
-
|
|
79
|
-
return this.#
|
|
81
|
+
getLastText() {
|
|
82
|
+
return this.#lastText
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
/**
|
|
83
86
|
* Starts a request to find all matches for the text in the page.
|
|
84
87
|
* @param {string} text Value to find in page.
|
|
88
|
+
* @param {boolean | void} skipInputUpdate Skip findbar input update.
|
|
85
89
|
*/
|
|
86
|
-
startFind(text) {
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
startFind(text, skipInputUpdate) {
|
|
91
|
+
skipInputUpdate || this.#window?.webContents.send('electron-findbar/text-change', text)
|
|
92
|
+
if (this.#lastText = text) {
|
|
93
|
+
this.isOpen() && this.#findableContents.findInPage(this.#lastText, { findNext: true })
|
|
89
94
|
} else {
|
|
90
95
|
this.stopFind()
|
|
91
96
|
}
|
|
@@ -95,22 +100,16 @@ class Findbar {
|
|
|
95
100
|
* Select previous match if any.
|
|
96
101
|
*/
|
|
97
102
|
findPrevious() {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this.#searchableContents.findInPage(this.#lastValue, { forward: false })
|
|
103
|
+
this.#matches.active === 1 && (this.#fixMove = false)
|
|
104
|
+
this.isOpen() && this.#findableContents.findInPage(this.#lastText, { forward: false })
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
/**
|
|
106
108
|
* Select next match if any.
|
|
107
109
|
*/
|
|
108
110
|
findNext() {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
this.#searchableContents.findInPage(this.#lastValue, { forward: true })
|
|
111
|
+
this.#matches.active === this.#matches.total && (this.#fixMove = true)
|
|
112
|
+
this.isOpen() && this.#findableContents.findInPage(this.#lastText, { forward: true })
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
/**
|
|
@@ -118,17 +117,33 @@ class Findbar {
|
|
|
118
117
|
*/
|
|
119
118
|
stopFind() {
|
|
120
119
|
this.isOpen() && this.#sendMatchesCount(0, 0)
|
|
121
|
-
this.#
|
|
120
|
+
this.#findableContents.isDestroyed() || this.#findableContents.stopFindInPage("clearSelection")
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
/**
|
|
125
|
-
*
|
|
124
|
+
* Whether the findbar is opened.
|
|
126
125
|
* @returns {boolean} True, if the findbar is open. Otherwise, false.
|
|
127
126
|
*/
|
|
128
127
|
isOpen() {
|
|
129
128
|
return !!this.#window
|
|
130
129
|
}
|
|
131
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Whether the findbar is focused. If the findbar is closed, false will be returned.
|
|
133
|
+
* @returns {boolean} True, if the findbar is focused. Otherwise, false.
|
|
134
|
+
*/
|
|
135
|
+
isFocused() {
|
|
136
|
+
return !!this.#window?.isFocused()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Whether the findbar is visible to the user in the foreground of the app. If the findbar is closed, false will be returned.
|
|
141
|
+
* @returns {boolean} True, if the findbar is visible. Otherwise, false.
|
|
142
|
+
*/
|
|
143
|
+
isVisible() {
|
|
144
|
+
return !!this.#window?.isVisible()
|
|
145
|
+
}
|
|
146
|
+
|
|
132
147
|
/**
|
|
133
148
|
* Provides a customized set of options to findbar window before open. Note
|
|
134
149
|
* that the options below are necessary for the correct functioning and cannot
|
|
@@ -157,86 +172,40 @@ class Findbar {
|
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
/**
|
|
160
|
-
* Set
|
|
161
|
-
*
|
|
162
|
-
* On darwin platform, the findbar follows the parent window by default. This method is set
|
|
163
|
-
* to false to not create a "move" event listener unnescessarily.
|
|
164
|
-
* @platform win32,linux
|
|
165
|
-
* @param {boolean} follow If true, the findbar will follow the parent window movement.
|
|
166
|
-
*/
|
|
167
|
-
followParentWindow(follow) {
|
|
168
|
-
this.#followParent = follow
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Merge custom, defaults, and fixed options.
|
|
173
|
-
* @param {Electron.BrowserWindowConstructorOptions} options Custom options.
|
|
174
|
-
* @param {BaseWindow | void} parent Parent window, if any.
|
|
175
|
-
* @returns {Electron.BrowserWindowConstructorOptions} Merged options.
|
|
175
|
+
* Set a bounds handler to calculate the findbar bounds when the parent resizes.
|
|
176
|
+
* @param {{parentBounds: Rectangle, findbarBounds: Rectangle} => Rectangle} boundsHandler Bounds handler.
|
|
176
177
|
*/
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
options.width = options.width ?? 372
|
|
180
|
-
options.height = options.height ?? 52
|
|
181
|
-
options.resizable = options.resizable ?? false
|
|
182
|
-
options.movable = options.movable ?? false
|
|
183
|
-
options.parent = parent
|
|
184
|
-
options.frame = false
|
|
185
|
-
options.transparent = true
|
|
186
|
-
options.maximizable = false
|
|
187
|
-
options.minimizable = false
|
|
188
|
-
options.skipTaskbar = true
|
|
189
|
-
options.fullscreenable = false
|
|
190
|
-
if (!options.webPreferences) { options.webPreferences = {} }
|
|
191
|
-
options.webPreferences.nodeIntegration = true
|
|
192
|
-
options.webPreferences.contextIsolation = false
|
|
193
|
-
return options
|
|
178
|
+
setBoundsHandler(boundsHandler) {
|
|
179
|
+
this.#positionHandler = boundsHandler
|
|
194
180
|
}
|
|
195
181
|
|
|
196
182
|
/**
|
|
197
183
|
* Register all event listeners.
|
|
198
184
|
*/
|
|
199
185
|
#registerListeners() {
|
|
200
|
-
const followParent = this.#followParent
|
|
201
186
|
const showCascade = () => this.#window.isVisible() || this.#window.show()
|
|
202
187
|
const hideCascade = () => this.#window.isVisible() && this.#window.hide()
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const newPos = this.#parent.getPosition()
|
|
207
|
-
const diff = { x: newPos[0] - lastPos[0], y: newPos[1] - lastPos[1] }
|
|
208
|
-
lastPos = newPos
|
|
209
|
-
|
|
210
|
-
const { x, y } = this.#window.getBounds()
|
|
211
|
-
this.#window.setPosition(x + diff.x, y + diff.y)
|
|
188
|
+
const positionHandler = () => {
|
|
189
|
+
const pos = this.#positionHandler(this.#parent.getBounds(), this.#window.getBounds())
|
|
190
|
+
this.#window.setPosition(pos.x, pos.y)
|
|
212
191
|
}
|
|
213
|
-
|
|
192
|
+
|
|
214
193
|
this.#parent.prependListener('show', showCascade)
|
|
215
194
|
this.#parent.prependListener('hide', hideCascade)
|
|
216
|
-
|
|
195
|
+
this.#parent.prependListener('resize', positionHandler)
|
|
196
|
+
this.#parent.prependListener('move', positionHandler)
|
|
217
197
|
|
|
218
198
|
this.#window.once('close', () => {
|
|
219
199
|
this.#parent.off('show', showCascade)
|
|
220
200
|
this.#parent.off('hide', hideCascade)
|
|
221
|
-
|
|
201
|
+
this.#parent.off('resize', positionHandler)
|
|
202
|
+
this.#parent.off('move', positionHandler)
|
|
222
203
|
this.#window = null
|
|
223
204
|
this.stopFind()
|
|
224
205
|
})
|
|
225
206
|
|
|
226
|
-
this.#
|
|
227
|
-
this.#
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Set default findbar position.
|
|
232
|
-
* @param {Rectangle} parentBounds
|
|
233
|
-
*/
|
|
234
|
-
#setDefaultPosition(parentBounds) {
|
|
235
|
-
const s = this.#window.getSize()
|
|
236
|
-
this.#window.setBounds({
|
|
237
|
-
x: parentBounds.x + parentBounds.width - s[0] - 20,
|
|
238
|
-
y: parentBounds.y - ((s[1] / 4) | 0)
|
|
239
|
-
})
|
|
207
|
+
this.#findableContents.prependOnceListener('destroyed', () => { this.close() })
|
|
208
|
+
this.#findableContents.prependListener('found-in-page', (_e, result) => { this.#sendMatchesCount(result.activeMatchOrdinal, result.matches) })
|
|
240
209
|
}
|
|
241
210
|
|
|
242
211
|
/**
|
|
@@ -256,31 +225,67 @@ class Findbar {
|
|
|
256
225
|
}
|
|
257
226
|
|
|
258
227
|
/**
|
|
259
|
-
*
|
|
228
|
+
* Focus the findbar and highlight the input text.
|
|
260
229
|
*/
|
|
261
|
-
#
|
|
230
|
+
#focusWindowAndHighlightInput() {
|
|
231
|
+
this.#window.focus()
|
|
262
232
|
this.#window.webContents.send('electron-findbar/input-focus')
|
|
263
233
|
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Set default findbar position.
|
|
237
|
+
* @param {Rectangle} parentBounds
|
|
238
|
+
* @param {Rectangle} findbarBounds
|
|
239
|
+
* @returns {x: number, y: number} position.
|
|
240
|
+
*/
|
|
241
|
+
static #setDefaultPosition(parentBounds, findbarBounds) {
|
|
242
|
+
return {
|
|
243
|
+
x: parentBounds.x + parentBounds.width - findbarBounds.width - 20,
|
|
244
|
+
y: parentBounds.y - ((findbarBounds.height / 4) | 0)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Merge custom, defaults, and fixed options.
|
|
250
|
+
* @param {Electron.BrowserWindowConstructorOptions} options Custom options.
|
|
251
|
+
* @param {BaseWindow | void} parent Parent window, if any.
|
|
252
|
+
* @returns {Electron.BrowserWindowConstructorOptions} Merged options.
|
|
253
|
+
*/
|
|
254
|
+
static #mergeStandardOptions(options, parent) {
|
|
255
|
+
if (!options) { options = {} }
|
|
256
|
+
options.width = options.width ?? 372
|
|
257
|
+
options.height = options.height ?? 52
|
|
258
|
+
options.resizable = options.resizable ?? false
|
|
259
|
+
options.movable = options.movable ?? false
|
|
260
|
+
options.acceptFirstMouse = options.acceptFirstMouse ?? true
|
|
261
|
+
options.parent = parent
|
|
262
|
+
options.frame = false
|
|
263
|
+
options.transparent = true
|
|
264
|
+
options.maximizable = false
|
|
265
|
+
options.minimizable = false
|
|
266
|
+
options.skipTaskbar = true
|
|
267
|
+
options.fullscreenable = false
|
|
268
|
+
if (!options.webPreferences) { options.webPreferences = {} }
|
|
269
|
+
options.webPreferences.nodeIntegration = true
|
|
270
|
+
options.webPreferences.contextIsolation = false
|
|
271
|
+
return options
|
|
272
|
+
}
|
|
264
273
|
}
|
|
265
274
|
|
|
266
275
|
/**
|
|
267
276
|
* Define IPC events.
|
|
268
277
|
*/
|
|
269
|
-
(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
ipcMain.on('electron-findbar/previous', e => e.sender.findbar.findPrevious())
|
|
278
|
-
ipcMain.on('electron-findbar/next', e => e.sender.findbar.findNext())
|
|
279
|
-
ipcMain.on('electron-findbar/close', e => {
|
|
280
|
-
const findbar = e.sender.findbar
|
|
278
|
+
(ipc => {
|
|
279
|
+
ipc.handle('electron-findbar/last-text', e => e.sender._findbar.getLastText())
|
|
280
|
+
ipc.on('electron-findbar/input-change', (e, text, skip) => e.sender._findbar.startFind(text, skip))
|
|
281
|
+
ipc.on('electron-findbar/previous', e => e.sender._findbar.findPrevious())
|
|
282
|
+
ipc.on('electron-findbar/next', e => e.sender._findbar.findNext())
|
|
283
|
+
ipc.on('electron-findbar/open', e => e.sender._findbar.open())
|
|
284
|
+
ipc.on('electron-findbar/close', e => {
|
|
285
|
+
const findbar = e.sender._findbar
|
|
281
286
|
findbar.stopFind()
|
|
282
287
|
findbar.close()
|
|
283
288
|
})
|
|
284
|
-
}) (require('electron'))
|
|
289
|
+
}) (require('electron').ipcMain)
|
|
285
290
|
|
|
286
291
|
module.exports = { Findbar }
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-findbar",
|
|
3
|
-
"version": "0.4
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"description": "Chrome-like findbar for your Electron app.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"sample": "electron sample.js",
|
|
8
|
-
"test": "node test.js"
|
|
7
|
+
"sample": "electron test/sample.js",
|
|
8
|
+
"test": "node test/test.js"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
@@ -13,10 +13,15 @@
|
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
15
15
|
"findbar",
|
|
16
|
+
"find-bar",
|
|
17
|
+
"find bar",
|
|
16
18
|
"electron-findbar",
|
|
19
|
+
"electron-find-bar",
|
|
17
20
|
"electron",
|
|
18
21
|
"chrome-findbar",
|
|
19
|
-
"
|
|
22
|
+
"chrome-find-bar",
|
|
23
|
+
"search",
|
|
24
|
+
"search-bar"
|
|
20
25
|
],
|
|
21
26
|
"author": "ECRomaneli",
|
|
22
27
|
"license": "MIT",
|
package/remote.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote IPC events to control the findbar through the renderer.
|
|
3
|
+
*/
|
|
4
|
+
const Remote = (ipc => ({
|
|
5
|
+
/**
|
|
6
|
+
* Get last queried text.
|
|
7
|
+
* @returns {string}
|
|
8
|
+
*/
|
|
9
|
+
getLastText: async () => ipc.invoke('electron-findbar/last-text'),
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Change the input value and find it.
|
|
13
|
+
* @param {string} text
|
|
14
|
+
*/
|
|
15
|
+
inputChange: (text) => { ipc.send('electron-findbar/input-change', text) },
|
|
16
|
+
previous: () => { ipc.send('electron-findbar/previous') },
|
|
17
|
+
next: () => { ipc.send('electron-findbar/next') },
|
|
18
|
+
open: () => { ipc.send('electron-findbar/open') },
|
|
19
|
+
close: () => { ipc.send('electron-findbar/close') },
|
|
20
|
+
})) (require('electron').ipcRenderer)
|
|
21
|
+
|
|
22
|
+
module.exports = Remote
|
package/test/sample.html
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>Sample</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<input type="text" oninput="$remote.inputChange(event.target.value)">
|
|
8
|
+
<button onclick="$remote.open()">Open</button>
|
|
9
|
+
<button onclick="$remote.previous()">Previous</button>
|
|
10
|
+
<button onclick="$remote.next()">Next</button>
|
|
11
|
+
<button onclick="$remote.close()">Close</button>
|
|
12
|
+
<br>
|
|
13
|
+
<span>
|
|
14
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec volutpat massa et suscipit tincidunt. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam dictum massa id sapien tristique, in venenatis neque sollicitudin. Fusce accumsan augue arcu, sed rhoncus libero pretium vitae. Phasellus sed imperdiet ante. Maecenas ultrices, elit vitae aliquet tincidunt, elit enim maximus libero, id lacinia ex enim consequat justo. Sed sodales tristique augue sed maximus. Integer tincidunt mi ac arcu tempus, sed accumsan lorem bibendum. Sed in dictum nibh. Pellentesque posuere dui pulvinar sodales scelerisque. Ut nisi magna, vulputate ornare bibendum pharetra, accumsan sed tortor. Proin rhoncus interdum tincidunt. Suspendisse diam eros, ultrices eu volutpat in, vestibulum quis nunc.
|
|
15
|
+
|
|
16
|
+
Sed sit amet dapibus eros. Quisque porttitor mi a nisl pretium molestie. Praesent tellus dui, vehicula a ex sed, faucibus congue mauris. Cras dictum, sapien tempus consequat luctus, dolor magna vestibulum risus, nec blandit diam nulla eget est. Curabitur vitae posuere dolor, accumsan vulputate felis. Cras eget iaculis ante. Nulla velit felis, aliquet vitae convallis nec, vehicula et nunc. Fusce dapibus vel eros non viverra. Fusce elit arcu, tempus eu enim in, rutrum mattis justo.
|
|
17
|
+
|
|
18
|
+
Cras justo tellus, imperdiet et felis sed, iaculis varius lacus. Pellentesque posuere feugiat nisl, eu vulputate tortor. Proin volutpat tortor erat, feugiat pretium justo aliquam non. Maecenas nec neque ultricies diam rhoncus ullamcorper a vitae mauris. Integer ultricies euismod leo, nec facilisis diam volutpat et. Nulla eleifend ante egestas, imperdiet elit at, malesuada velit. Donec tincidunt eleifend libero. Integer congue pharetra scelerisque. In egestas lacus erat. Quisque aliquam massa lectus, eu semper massa ornare auctor. Cras sit amet auctor sem. Curabitur vitae tellus eu risus ultrices accumsan. Curabitur egestas eu lorem et efficitur. Sed nec turpis felis.
|
|
19
|
+
|
|
20
|
+
Suspendisse vel euismod ante. Nunc sagittis quam ut gravida pulvinar. Sed at semper nisl, eu porttitor ante. Cras vitae dolor massa. Fusce tincidunt turpis at egestas pharetra. Donec vitae vestibulum ante. Cras erat dolor, finibus vitae auctor vel, varius dictum arcu. Nam porta arcu consectetur, posuere tellus at, laoreet metus. Ut faucibus tincidunt mi placerat fermentum.
|
|
21
|
+
|
|
22
|
+
Aliquam ut pellentesque tellus, quis vulputate nisl. Phasellus vitae blandit nunc, eu sodales velit. Duis enim tellus, faucibus id arcu vitae, consectetur tempus mauris. Praesent commodo commodo dolor non malesuada. Donec sed dolor eget arcu tincidunt efficitur sit amet vel nisi. Sed consectetur tincidunt molestie. Nam at magna a odio rhoncus convallis id eu nunc. Nam luctus ut leo et viverra. Proin tempor libero vitae arcu laoreet, sed pharetra sapien rutrum. Nam nunc orci, aliquet sit amet dignissim nec, aliquet quis quam. Nulla facilisi.
|
|
23
|
+
</span>
|
|
24
|
+
<script>
|
|
25
|
+
const $remote = require('../remote')
|
|
26
|
+
</script>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
package/test/sample.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { BrowserWindow, app, Menu, MenuItem } = require('electron')
|
|
2
|
+
const { Findbar } = require('../index')
|
|
3
|
+
|
|
4
|
+
app.whenReady().then(() => {
|
|
5
|
+
const window = setupWindow()
|
|
6
|
+
const findbar = setupFindbar(window)
|
|
7
|
+
setupApplicationMenu(findbar)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function setupWindow() {
|
|
11
|
+
const window = new BrowserWindow({
|
|
12
|
+
webPreferences: {
|
|
13
|
+
nodeIntegration: true,
|
|
14
|
+
contextIsolation: false
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
window.loadFile(`${__dirname}/sample.html`)
|
|
18
|
+
return window
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function setupFindbar(window) {
|
|
22
|
+
const findbar = new Findbar(window)
|
|
23
|
+
findbar.setWindowOptions({ movable: true, resizable: true })
|
|
24
|
+
findbar.setWindowHandler(win => { /* handle the findbar window */ })
|
|
25
|
+
return findbar
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function setupApplicationMenu(findbar) {
|
|
29
|
+
const appMenu = Menu.getApplicationMenu()
|
|
30
|
+
appMenu.append(new MenuItem({ label: 'Findbar', submenu: [
|
|
31
|
+
{ label: 'Open', click: () => findbar.open(), accelerator: 'CommandOrControl+F' },
|
|
32
|
+
{ label: 'Close', click: () => findbar.isOpen() && findbar.close(), accelerator: 'Esc' },
|
|
33
|
+
{ role: 'toggleDevTools', accelerator: 'CommandOrControl+Shift+I' },
|
|
34
|
+
{ label: 'Test input propagation', click: () => {
|
|
35
|
+
let count = 0
|
|
36
|
+
setInterval(() => {
|
|
37
|
+
findbar.startFind('count: ' + count++)
|
|
38
|
+
findbar.startFind('cannot show this', true)
|
|
39
|
+
}, 1000)
|
|
40
|
+
}}
|
|
41
|
+
]}))
|
|
42
|
+
Menu.setApplicationMenu(appMenu)
|
|
43
|
+
}
|
package/web/app.css
CHANGED
|
@@ -16,6 +16,9 @@ nav {
|
|
|
16
16
|
--input-color: #1f1f1f;
|
|
17
17
|
--btn-hover-color: #ccc;
|
|
18
18
|
--btn-active-color: #bbb;
|
|
19
|
+
--font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
|
20
|
+
--font-size: .75rem;
|
|
21
|
+
--spacing: .75rem;
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
@media (prefers-color-scheme: dark) {
|
|
@@ -42,19 +45,25 @@ nav {
|
|
|
42
45
|
align-items: center;
|
|
43
46
|
width: 100%;
|
|
44
47
|
height: 100%;
|
|
45
|
-
padding:
|
|
46
|
-
font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";
|
|
48
|
+
padding: var(--spacing);
|
|
47
49
|
background-color: var(--bg-color);
|
|
48
50
|
color: var(--color);
|
|
49
|
-
font-size: .75rem;
|
|
50
51
|
border-radius: 10px;
|
|
51
52
|
border: 1px solid var(--border);
|
|
52
53
|
-webkit-app-region: drag;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
nav >
|
|
56
|
-
|
|
57
|
-
margin-right:
|
|
56
|
+
nav > *:not(:last-child),
|
|
57
|
+
.btn-group > *:not(:last-child) {
|
|
58
|
+
margin-right: var(--spacing);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
span, input {
|
|
62
|
+
font-family: var(--font-family);
|
|
63
|
+
font-size: var(--font-size);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
span {
|
|
58
67
|
user-select: none;
|
|
59
68
|
}
|
|
60
69
|
|
|
@@ -62,6 +71,7 @@ input {
|
|
|
62
71
|
width: 100%;
|
|
63
72
|
background-color: transparent;
|
|
64
73
|
color: var(--input-color);
|
|
74
|
+
font-weight: 500;
|
|
65
75
|
border: none;
|
|
66
76
|
outline: none;
|
|
67
77
|
-webkit-app-region: no-drag;
|
|
@@ -77,10 +87,6 @@ input {
|
|
|
77
87
|
display: flex;
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
.btn-group > :not(:last-child) {
|
|
81
|
-
margin-right: .5rem;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
90
|
.btn-group > div {
|
|
85
91
|
border-radius: 50%;
|
|
86
92
|
cursor: default;
|
package/web/app.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
1
|
+
const $remote = (ipc => ({
|
|
2
|
+
getLastText: async () => ipc.invoke('electron-findbar/last-text'),
|
|
3
|
+
inputChange: (value) => { ipc.send('electron-findbar/input-change', value, true) },
|
|
4
|
+
previous: () => { ipc.send('electron-findbar/previous') },
|
|
5
|
+
next: () => { ipc.send('electron-findbar/next') },
|
|
6
|
+
close: () => { ipc.send('electron-findbar/close') },
|
|
7
|
+
onMatchesChange: (listener) => { ipc.on('electron-findbar/matches', listener) },
|
|
8
|
+
onInputFocus: (listener) => { ipc.on('electron-findbar/input-focus', listener) },
|
|
9
|
+
onTextChange: (listener) => { ipc.on('electron-findbar/text-change', listener) }
|
|
10
|
+
})) (require('electron').ipcRenderer)
|
|
12
11
|
|
|
13
12
|
let canRequest = true, canMove = false
|
|
14
13
|
|
|
@@ -40,12 +39,15 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
40
39
|
}
|
|
41
40
|
})
|
|
42
41
|
|
|
43
|
-
$remote.onInputFocus(
|
|
42
|
+
$remote.onInputFocus(() => {
|
|
44
43
|
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
45
44
|
inputEl.focus()
|
|
46
45
|
})
|
|
47
46
|
|
|
48
|
-
inputEl.value =
|
|
47
|
+
$remote.onTextChange((_, text) => { inputEl.value = text })
|
|
48
|
+
|
|
49
|
+
inputEl.value = await $remote.getLastText()
|
|
50
|
+
$remote.inputChange(inputEl.value)
|
|
49
51
|
inputEl.setSelectionRange(0, inputEl.value.length)
|
|
50
52
|
inputEl.focus()
|
|
51
53
|
})
|
package/web/findbar.html
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="stylesheet" href="app.css">
|
|
9
9
|
<body>
|
|
10
10
|
<nav>
|
|
11
|
-
<input id='input' oninput="inputChange(event)" type="
|
|
11
|
+
<input id='input' oninput="inputChange(event)" type="text" spellcheck="false">
|
|
12
12
|
<span id="matches"></span>
|
|
13
13
|
<div class="divider"></div>
|
|
14
14
|
<div class="btn-group">
|
package/sample.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
const { BrowserWindow, app, Menu } = require('electron')
|
|
2
|
-
const { Findbar } = require('./index')
|
|
3
|
-
|
|
4
|
-
app.whenReady().then(() => {
|
|
5
|
-
const window = new BrowserWindow()
|
|
6
|
-
window.loadURL('https://github.com/ECRomaneli/electron-findbar#readme')
|
|
7
|
-
|
|
8
|
-
const findbar = new Findbar(window)
|
|
9
|
-
findbar.setWindowOptions({ movable: !true, resizable: true })
|
|
10
|
-
findbar.setWindowHandler(win => {
|
|
11
|
-
win.webContents.openDevTools()
|
|
12
|
-
})
|
|
13
|
-
findbar.open()
|
|
14
|
-
|
|
15
|
-
const contextMenu = Menu.buildFromTemplate([
|
|
16
|
-
{ role: 'separator' },
|
|
17
|
-
{ label: 'Open findbar', click: () => findbar.open(), accelerator: 'CommandOrControl+F' },
|
|
18
|
-
{ label: 'Close findbar', click: () => findbar.isOpen() && findbar.close(), accelerator: 'Esc', registerAccelerator: true, acceleratorWorksWhenHidden: true }
|
|
19
|
-
])
|
|
20
|
-
|
|
21
|
-
Menu.setApplicationMenu(contextMenu)
|
|
22
|
-
|
|
23
|
-
})
|
|
File without changes
|