diff --git a/src/book-viewer.js b/src/book-viewer.js
index 0611b26b..2e2735ef 100644
--- a/src/book-viewer.js
+++ b/src/book-viewer.js
@@ -19,6 +19,7 @@ import './navbar.js'
import {
AnnotationPopover, AnnotationModel, BookmarkModel, exportAnnotations,
} from './annotations.js'
+import { SelectionPopover } from './selection-tools.js'
import { ImageViewer } from './image-viewer.js'
import { makeBookInfoWindow } from './book-info.js'
import { getURIStore, getBookList } from './library.js'
@@ -577,12 +578,6 @@ GObject.registerClass({
grab_focus() { return this.#webView.grab_focus() }
})
-const SelectionPopover = GObject.registerClass({
- GTypeName: 'FoliateSelectionPopover',
- Template: pkg.moduleuri('ui/selection-popover.ui'),
-}, class extends Gtk.PopoverMenu {
-})
-
const autohide = (revealer, shouldStayVisible) => {
const show = () => revealer.reveal_child = true
const hide = () => revealer.reveal_child = false
@@ -951,7 +946,7 @@ export const BookViewer = GObject.registerClass({
this.#data.storage.set('lastLocation', cfi)
}
}
- #showSelection({ type, value, text, pos: { point, dir } }) {
+ #showSelection({ type, value, text, lang, pos: { point, dir } }) {
if (type === 'annotation') return new Promise(resolve => {
const annotation = this.#data.annotations.get(value)
const popover = utils.connect(new AnnotationPopover({ annotation }), {
@@ -991,10 +986,14 @@ export const BookViewer = GObject.registerClass({
},
'print': () => resolve('print'),
}))
- // it seems `closed` is emitted before the actions are run
- // so it needs the timeout
- popover.connect('closed', () => setTimeout(() =>
- resolved ? null : resolve(), 0))
+ utils.connect(popover, {
+ 'show-popover': (_, popover) =>
+ this._view.showPopover(popover, point, dir),
+ 'run-tool': () => ({ text, lang }),
+ // it seems `closed` is emitted before the actions are run
+ // so it needs the timeout
+ 'closed': () => setTimeout(() => resolved ? null : resolve(), 0),
+ })
this._view.showPopover(popover, point, dir)
})
}
diff --git a/src/reader/reader.js b/src/reader/reader.js
index c5f87de0..0203c34f 100644
--- a/src/reader/reader.js
+++ b/src/reader/reader.js
@@ -33,6 +33,12 @@ const getSelectionRange = doc => {
return range
}
+const getLang = el => {
+ const lang = el.lang || el?.getAttributeNS?.('http://www.w3.org/XML/1998/namespace', 'lang')
+ if (lang) return lang
+ if (el.parentElement) return getLang(el.parentElement)
+}
+
const blobToBase64 = blob => new Promise(resolve => {
const reader = new FileReader()
reader.readAsDataURL(blob)
@@ -363,7 +369,8 @@ class Reader {
if (!range) return
const pos = getPosition(range)
const value = this.view.getCFI(index, range)
- this.#showSelection({ range, value, pos })
+ const lang = getLang(range.commonAncestorContainer)
+ this.#showSelection({ range, lang, value, pos })
})
if (!this.view.isFixedLayout)
@@ -386,9 +393,9 @@ class Reader {
this.#showSelection({ range, value, pos })
})
}
- #showSelection({ range, value, pos }) {
+ #showSelection({ range, lang, value, pos }) {
const text = range.toString()
- globalThis.showSelection({ type: 'selection', text, value, pos })
+ globalThis.showSelection({ type: 'selection', text, lang, value, pos })
.then(action => {
if (action === 'copy') getHTML(range).then(html =>
emit({ type: 'selection', action, text, html }))
diff --git a/src/selection-tools.js b/src/selection-tools.js
new file mode 100644
index 00000000..e29b0af4
--- /dev/null
+++ b/src/selection-tools.js
@@ -0,0 +1,232 @@
+import Gtk from 'gi://Gtk'
+import Gio from 'gi://Gio'
+import GObject from 'gi://GObject'
+import WebKit from 'gi://WebKit'
+import Gdk from 'gi://Gdk'
+import { gettext as _ } from 'gettext'
+
+import * as utils from './utils.js'
+import { WebView } from './webview.js'
+
+const tools = {
+ 'dictionary': {
+ label: _('Dictionary'),
+ run: ({ text, lang }) => {
+ const { language } = new Intl.Locale(lang)
+ return `
+
+
+
+
+`
+ },
+ },
+}
+
+const SelectionToolPopover = GObject.registerClass({
+ GTypeName: 'FoliateSelectionToolPopover',
+}, class extends Gtk.Popover {
+ #webView = utils.connect(new WebView({
+ settings: new WebKit.Settings({
+ enable_write_console_messages_to_stdout: true,
+ enable_back_forward_navigation_gestures: false,
+ enable_hyperlink_auditing: false,
+ enable_html5_database: false,
+ enable_html5_local_storage: false,
+ }),
+ }), {
+ 'decide-policy': (_, decision, type) => {
+ switch (type) {
+ case WebKit.PolicyDecisionType.NAVIGATION_ACTION:
+ case WebKit.PolicyDecisionType.NEW_WINDOW_ACTION: {
+ const { uri } = decision.navigation_action.get_request()
+ if (!uri.startsWith('foliate:')) {
+ decision.ignore()
+ Gtk.show_uri(null, uri, Gdk.CURRENT_TIME)
+ return true
+ }
+ }
+ }
+ },
+ })
+ constructor(params) {
+ super(params)
+ Object.assign(this, {
+ width_request: 300,
+ height_request: 300,
+ })
+ this.child = this.#webView
+ this.#webView.set_background_color(new Gdk.RGBA())
+ }
+ load(html) {
+ this.#webView.loadHTML(html, 'foliate:selection-tool')
+ .then(() => this.#webView.opacity = 1)
+ .catch(e => console.error(e))
+ }
+})
+
+const getSelectionToolPopover = utils.memoize(() => new SelectionToolPopover())
+
+export const SelectionPopover = GObject.registerClass({
+ GTypeName: 'FoliateSelectionPopover',
+ Template: pkg.moduleuri('ui/selection-popover.ui'),
+ Signals: {
+ 'show-popover': { param_types: [Gtk.Popover.$gtype] },
+ 'run-tool': { return_type: GObject.TYPE_JSOBJECT },
+ },
+}, class extends Gtk.PopoverMenu {
+ constructor(params) {
+ super(params)
+ const model = this.menu_model
+ const section = new Gio.Menu()
+ model.insert_section(1, null, section)
+
+ const group = new Gio.SimpleActionGroup()
+ this.insert_action_group('selection-tools', group)
+
+ for (const [name, tool] of Object.entries(tools)) {
+ const action = new Gio.SimpleAction({ name })
+ action.connect('activate', () => {
+ const popover = getSelectionToolPopover()
+ Promise.resolve(tool.run(this.emit('run-tool')))
+ .then(x => popover.load(x))
+ .catch(e => console.error(e))
+ this.emit('show-popover', popover)
+ })
+ group.add_action(action)
+ section.append(tool.label, `selection-tools.${name}`)
+ }
+ }
+})
diff --git a/src/ui/selection-popover.ui b/src/ui/selection-popover.ui
index cc16680d..773f968a 100644
--- a/src/ui/selection-popover.ui
+++ b/src/ui/selection-popover.ui
@@ -19,20 +19,6 @@
edit-find-symbolic
-
- -
- Dictionary
- selection.dictionary
-
- -
- Wikipedia
- selection.wikipedia
-
- -
- Translate
- selection.translate
-
-