From e8d1b9915a60732b764d6c18efd184b5ae73c06b Mon Sep 17 00:00:00 2001 From: Michal Klocek <michal.klocek@theqtcompany.com> Date: Wed, 13 Apr 2016 16:06:23 +0200 Subject: [PATCH] Add widget based spellchecker example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This example demonstrates how to convert 'dic' dictionary files into 'bdict' binary format using qwebengine_convert_dict tool. It shows how to implement language selection. It adds two dummy dictionaries. Change-Id: Iffc23a0ed4e51cbc749f666c8f565fafb3739a9b Reviewed-by: Michael Brüning <michael.bruning@qt.io> --- examples/examples.pro | 7 +- .../spellchecker/data/icon.svg | 24 ++++ .../spellchecker/data/index.html | 33 +++++ .../spellchecker/data/spellchecker.qrc | 7 + .../spellchecker/data/style.css | 90 ++++++++++++ .../spellchecker/dict/de/README.txt | 20 +++ .../spellchecker/dict/de/de-DE.aff | 5 + .../spellchecker/dict/de/de-DE.dic | 14 ++ .../spellchecker/dict/en/README.txt | 16 +++ .../spellchecker/dict/en/en-US.aff | 5 + .../spellchecker/dict/en/en-US.dic | 11 ++ .../doc/images/spellchecker-example.png | Bin 0 -> 15978 bytes .../spellchecker/doc/src/spellchecker.qdoc | 134 ++++++++++++++++++ .../webenginewidgets/spellchecker/main.cpp | 54 +++++++ .../spellchecker/spellchecker.pro | 43 ++++++ .../webenginewidgets/spellchecker/webview.cpp | 96 +++++++++++++ .../webenginewidgets/spellchecker/webview.h | 60 ++++++++ src/webengine/doc/qtwebengine.qdocconf | 1 + src/webengine/doc/src/external-resources.qdoc | 5 + 19 files changed, 624 insertions(+), 1 deletion(-) create mode 100644 examples/webenginewidgets/spellchecker/data/icon.svg create mode 100644 examples/webenginewidgets/spellchecker/data/index.html create mode 100644 examples/webenginewidgets/spellchecker/data/spellchecker.qrc create mode 100644 examples/webenginewidgets/spellchecker/data/style.css create mode 100644 examples/webenginewidgets/spellchecker/dict/de/README.txt create mode 100644 examples/webenginewidgets/spellchecker/dict/de/de-DE.aff create mode 100644 examples/webenginewidgets/spellchecker/dict/de/de-DE.dic create mode 100644 examples/webenginewidgets/spellchecker/dict/en/README.txt create mode 100644 examples/webenginewidgets/spellchecker/dict/en/en-US.aff create mode 100644 examples/webenginewidgets/spellchecker/dict/en/en-US.dic create mode 100644 examples/webenginewidgets/spellchecker/doc/images/spellchecker-example.png create mode 100644 examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc create mode 100644 examples/webenginewidgets/spellchecker/main.cpp create mode 100644 examples/webenginewidgets/spellchecker/spellchecker.pro create mode 100644 examples/webenginewidgets/spellchecker/webview.cpp create mode 100644 examples/webenginewidgets/spellchecker/webview.h diff --git a/examples/examples.pro b/examples/examples.pro index 0de2ec505..f15007e83 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -13,5 +13,10 @@ qtHaveModule(webenginewidgets) { webenginewidgets/cookiebrowser \ webenginewidgets/demobrowser \ webenginewidgets/markdowneditor \ - webenginewidgets/simplebrowser \ + webenginewidgets/simplebrowser + + !contains(WEBENGINE_CONFIG, no_spellcheck):!osx:!cross_compile { + SUBDIRS += webenginewidgets/spellchecker + } + } diff --git a/examples/webenginewidgets/spellchecker/data/icon.svg b/examples/webenginewidgets/spellchecker/data/icon.svg new file mode 100644 index 000000000..b90ff26dd --- /dev/null +++ b/examples/webenginewidgets/spellchecker/data/icon.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="94px" height="94px" viewBox="0 0 94 94" enable-background="new 0 0 94 94" xml:space="preserve"> +<g> + <circle fill="none" cx="47" cy="47" r="47"/> + <g> + <path fill="#46A2DA" d="M47,92.979c-11.779,0-23.559-4.484-32.526-13.451C-3.461,61.591-3.461,32.409,14.472,14.474 + C32.41-3.463,61.592-3.461,79.526,14.473c17.935,17.936,17.935,47.119,0.002,65.054l-0.002,0.001 + C70.559,88.495,58.779,92.979,47,92.979z"/> + </g> + <path fill="#80C342" d="M93,47C93,21.595,72.405,1,47,1C34.297,1,22.797,6.149,14.473,14.473l65.054,65.054 + C87.851,71.203,93,59.703,93,47z"/> + <g> + <path fill="#46A2DA" d="M47,65c-4.808,0-9.328-1.873-12.728-5.272c-7.018-7.019-7.018-18.438,0-25.456 + C37.672,30.873,42.192,29,47,29s9.328,1.873,12.728,5.272c7.018,7.019,7.018,18.438,0,25.456C56.328,63.127,51.808,65,47,65z"/> + <path fill="#FFFFFF" d="M62.248,59.919c6.671-7.858,6.312-19.644-1.105-27.061C57.237,28.953,52.118,27,47,27 + c-5.118,0-10.237,1.953-14.142,5.858c-7.81,7.81-7.81,20.474,0,28.284C36.763,65.047,41.882,67,47,67 + c4.379,0,8.752-1.441,12.372-4.3L77.88,81.209c0.989-0.895,1.935-1.837,2.843-2.814L62.248,59.919z M35.686,58.314 + c-6.238-6.238-6.238-16.389,0-22.627C38.708,32.664,42.726,31,47,31c4.274,0,8.292,1.664,11.314,4.686 + c6.238,6.238,6.238,16.389,0,22.627C55.292,61.336,51.274,63,47,63C42.726,63,38.708,61.336,35.686,58.314z"/> + </g> +</g> +</svg> diff --git a/examples/webenginewidgets/spellchecker/data/index.html b/examples/webenginewidgets/spellchecker/data/index.html new file mode 100644 index 000000000..b6cec5fe0 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/data/index.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Contact us</title> + <link rel="stylesheet" type="text/css" href="style.css"> + </head> + <body> + <form class="form"> + <img class="logo" src="icon.svg" alt="qtwebengine"> + <div class="header"> + <h1>Contact us</h1> + <h2>We are here to help</h2> + </div> + <label> + <span>First Name:</span><input id="firstname" type="text" name="name" /> + </label> + <label> + <span>Last Name:</span><input id="lastName" type="text" name="name" /> + </label> + <label> + <span>Email Address:</span><input id="email" type="text" name="email" /> + </label> + <label> + <span>Subject:</span><input id="subject" type="text" name="subject" /> + </label> + <label> + <span>Message:</span><textarea id="feedback" name="feedback"></textarea> + </label> + <input type="submit" value="Send" /> + </form> + </body> +</html> diff --git a/examples/webenginewidgets/spellchecker/data/spellchecker.qrc b/examples/webenginewidgets/spellchecker/data/spellchecker.qrc new file mode 100644 index 000000000..a9c76cc7e --- /dev/null +++ b/examples/webenginewidgets/spellchecker/data/spellchecker.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/"> + <file>index.html</file> + <file>style.css</file> + <file>icon.svg</file> + </qresource> +</RCC> diff --git a/examples/webenginewidgets/spellchecker/data/style.css b/examples/webenginewidgets/spellchecker/data/style.css new file mode 100644 index 000000000..7b6736f1f --- /dev/null +++ b/examples/webenginewidgets/spellchecker/data/style.css @@ -0,0 +1,90 @@ +.logo { + width: 50px; + height: 50px; + float: left; + margin: 20px 20px 0px 20px; + -webkit-animation:spin 8s linear infinite; +} +@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } + +.header { + display: inline +} + +.form { + width: 450px; + height: 600px; + background: -webkit-linear-gradient(bottom, #ddd, #fff); + border: 1px solid #999; + border-radius: 12px; + color: #46a; + font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif; + font-size: 14px; + font-style: italic; + font-weight: bold; + margin: auto; + padding: 10px; + position: relative; + line-height: 26px; + text-decoration: none; + -webkit-box-shadow: 0px 0px 5px #444; +} + +h1 { + padding-left:40px; + color: #46a2da; +} + +h2 { + color: #80c342; + font-size: 13px; + margin-top: -20px; +} + +span { + margin-left: 20px; +} + +input { + width: 400px; + display: block; + border: 1px solid #999; + height: 25px; + margin-left: 20px; + margin-bottom: 10px; + padding-left: 10px; + -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); +} + +textarea { + width: 400px; + max-width: 400px; + height: 180px; + max-height: 400px; + display: block; + margin-left: 20px; + padding-left: 10px; + padding-right: 10px; + -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); +} + +input:focus, textarea:focus { + outline:none; + border: 1px solid #46a2da; +} + +input[type=submit] { + width: 100px; + left: 170px; + bottom: 10px; + background: #46a2da; + color: #fff; + height: 30px; + position: absolute; + border-radius: 14px; +} + +input[type=submit]:hover { + background: #80c342; +} + diff --git a/examples/webenginewidgets/spellchecker/dict/de/README.txt b/examples/webenginewidgets/spellchecker/dict/de/README.txt new file mode 100644 index 000000000..f9c94e06d --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/de/README.txt @@ -0,0 +1,20 @@ +This is dummy german dictionary which knows only following words: + +* ich +* du +* er +* sie +* es +* wir +* ihr +* sie +* Sie +* liebe +* liebst +* liebt +* lieben +* liebt +* qt + +Also each of words above can start with 'q' for example +* qich diff --git a/examples/webenginewidgets/spellchecker/dict/de/de-DE.aff b/examples/webenginewidgets/spellchecker/dict/de/de-DE.aff new file mode 100644 index 000000000..ff8185771 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/de/de-DE.aff @@ -0,0 +1,5 @@ +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzqESIANRTOLCDUGMPHBYFVKWZQ + +PFX Q Y 1 +PFX Q 0 q . diff --git a/examples/webenginewidgets/spellchecker/dict/de/de-DE.dic b/examples/webenginewidgets/spellchecker/dict/de/de-DE.dic new file mode 100644 index 000000000..d10ae2600 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/de/de-DE.dic @@ -0,0 +1,14 @@ +15 +du/Q +er/Q +es/Q +ich/Q +ihr/Q +liebe/Q +lieben/Q +liebst/Q +liebt/Q +qt/Q +sie/Q +Sie/Q +wir/Q diff --git a/examples/webenginewidgets/spellchecker/dict/en/README.txt b/examples/webenginewidgets/spellchecker/dict/en/README.txt new file mode 100644 index 000000000..41b529292 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/en/README.txt @@ -0,0 +1,16 @@ +This is dummy english dictionary which knows only following words: + +* I +* you +* he +* she +* it +* we +* they +* love +* loves +* qt + +Also each of words above can start with 'q' for example: + +* qI diff --git a/examples/webenginewidgets/spellchecker/dict/en/en-US.aff b/examples/webenginewidgets/spellchecker/dict/en/en-US.aff new file mode 100644 index 000000000..ff8185771 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/en/en-US.aff @@ -0,0 +1,5 @@ +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzqESIANRTOLCDUGMPHBYFVKWZQ + +PFX Q Y 1 +PFX Q 0 q . diff --git a/examples/webenginewidgets/spellchecker/dict/en/en-US.dic b/examples/webenginewidgets/spellchecker/dict/en/en-US.dic new file mode 100644 index 000000000..3d4ecdfa4 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/dict/en/en-US.dic @@ -0,0 +1,11 @@ +10 +he/Q +I/Q +it/Q +love/Q +loves/Q +qt/Q +she/Q +they/Q +we/Q +you/Q diff --git a/examples/webenginewidgets/spellchecker/doc/images/spellchecker-example.png b/examples/webenginewidgets/spellchecker/doc/images/spellchecker-example.png new file mode 100644 index 0000000000000000000000000000000000000000..cc4e74946ae37aad9892c9c839bc8a5265227386 GIT binary patch literal 15978 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 Y0FGYSZ(uY<{Qv*}07*qoM6N<$g3{todjJ3c literal 0 HcmV?d00001 diff --git a/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc b/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc new file mode 100644 index 000000000..d5b972b93 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/doc/src/spellchecker.qdoc @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example webenginewidgets/spellchecker + \title WebEngine Widgets Spellchecker Example + \ingroup webengine-widgetexamples + \brief Integrates a spellchecker into a simple HTML form. + + \image spellchecker-example.png + + \e {Spellchecker} demonstrates how to integrate spellchecking support into + an HTML form that enables users to submit spellchecked messages. + + \include examples-run.qdocinc + + \section1 Dictionaries + + To be able to check the spelling, we need to provide the spellchecker with + dictionaries. In this example, we want to support the English and German + languages. + + The Qt WebEngine spellchecker supports dictionaries from the + \l{Hunspell project}, but they have to be compiled into a special binary + format. A Hunspell dictionary consists of two files: + + \list + + \li A \c .dic file that is a dictionary containing words for the + language + \li An \c .aff file that defines the meaning of special flags in the + dictionary + \endlist + + These two files can be converted into the \c bdic format by using the + \c qwebengine_convert_dict tool that is shipped together with Qt. + + In this example, we are going to compile en_US and de_DE dictionaries. + However, the real full dictionaries would take too much space for the + purposes of this example. Therefore, we have created two dummy dictionaries + that contain the following words and can be used to demonstrate the + conversion process: + + \list + \li English dictionary: I, you, he, she, it, we, they, love, loves, qt + \li German dictionary: ich, du, er, sie, es, wir, ihr, sie, Sie, liebe, + liebst, liebt, lieben, liebt, qt + \endlist + + Each word in a dictionary can be prefixed with \c q. For more information + about how to create \c dic and \c aff files, see the Hunspell dictionary + file format specification in the \l{Hunspell Project}. + + When a specific spellchecking language is requested, Qt WebEngine will try + to load the already compiled matching \c .bdic file first from + \e qtwebengine_dictionaries directories relative to the executable, + then it will look in \c QT_INSTALL_PREFIX/qtwebengines_dictionaries. + + We specify the QMAKE_EXTRA_COMPILERS parameter in the project file to add a + conversion step to the build process: + + \quotefromfile webenginewidgets/spellchecker/spellchecker.pro + \skipto CONVERT_TOOL + \printuntil QMAKE_EXTRA_COMPILERS + + To set up a dictionary, we run \c qwebengine_convert_dic passing the + file path of the dictionary \c dic and \c bdic files. The \c aff file and + optional \c delta file are also picked up by the \c convert process. + The output \c bdic file is placed into the \e qtwebengine_dictionaries local + directory, which the application binary will run from. + + \section1 Setting the Spellchecker + + The constructor of our class is trivial. + + \quotefromfile webenginewidgets/spellchecker/webview.cpp + \skipto WebView::WebView + \printuntil } + We define simple mapping between our dictionary filenames and + the actual language display name. We will use that mapping to display names + of dictionaries in the context menu. Spellchecking is disabled by default. + Therefore we also enable spellchecker and set the \e English dictionary. + When Qt WebEngine's spellcheck service initializes, it will try to load the + \c bdict dictionaries and to check them for consistency. + Any errors are logged by using the qWarning() function. + + \section1 Switching the Spellchecking Language + + The current language used for spellchecking is defined per profile, and can + get set using the QWebEngineProfile::setSpellCheckLanguage method. When the + user clicks on an underlined misspelled word, the default context menu + displays up to four suggestions. Selecting one will replace the misspelled + word. We could reimplement a number of suggestions, by overriding + QWebEngineView::contextMenuEvent and using + QWebEngineContextMenuData::spellCheckerSuggestions, but we will demonstrate + how to add langague options in the context menu instead: + + \quotefromfile webenginewidgets/spellchecker/webview.cpp + \skipto void WebView::contextMenuEvent + \printuntil menu->popup + \printline } + + Above, we get the QWebEngineContextMenuData instance using the + QWebEnginePage::contextMenuData method. We use it to be notified when the + user clicks on an editable field and show the \uicontrol {Check Spelling} + item in the context menu. Moreover, if spellchecking is enabled, we also + add the \uicontrol {Select Language} submenu with the supported languages. + When an action is triggered, we set the language with the + QWebEngineProfile::setSpellCheckLanguage call. +*/ diff --git a/examples/webenginewidgets/spellchecker/main.cpp b/examples/webenginewidgets/spellchecker/main.cpp new file mode 100644 index 000000000..0d411f665 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/main.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "webview.h" +#include <QApplication> + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + WebView view; + view.setUrl(QUrl(QStringLiteral("qrc:/index.html"))); + view.resize(500, 640); + view.show(); + + return app.exec(); +} diff --git a/examples/webenginewidgets/spellchecker/spellchecker.pro b/examples/webenginewidgets/spellchecker/spellchecker.pro new file mode 100644 index 000000000..c7bee6584 --- /dev/null +++ b/examples/webenginewidgets/spellchecker/spellchecker.pro @@ -0,0 +1,43 @@ +TEMPLATE = app +TARGET = spellchecker +QT += webenginewidgets +CONFIG += c++11 + +HEADERS += \ + webview.h + +SOURCES += \ + main.cpp \ + webview.cpp + +RESOURCES += \ + data/spellchecker.qrc + +DISTFILES += \ + dict/en/README.txt \ + dict/en/en-US.dic \ + dict/en/en-US.aff \ + dict/de/README.txt \ + dict/de/de-DE.dic \ + dict/de/de-DE.aff + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/spellchecker +INSTALLS += target + +qtPrepareTool(CONVERT_TOOL, qwebengine_convert_dict) + +debug_and_release { + CONFIG(debug, debug|release): DICTIONARIES_DIR = debug/qtwebengine_dictionaries + else: DICTIONARIES_DIR = release/qtwebengine_dictionaries +} else { + DICTIONARIES_DIR = qtwebengine_dictionaries +} + +dict.files = $$PWD/dict/en/en-US.dic $$PWD/dict/de/de-DE.dic +dictoolbuild.input = dict.files +dictoolbuild.output = $${DICTIONARIES_DIR}/${QMAKE_FILE_BASE}.bdic +dictoolbuild.depends = ${QMAKE_FILE_PATH}/${QMAKE_FILE_BASE}.aff +dictoolbuild.commands = $${CONVERT_TOOL} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +dictoolbuild.name = Build ${QMAKE_FILE_IN_BASE} +dictoolbuild.CONFIG = no_link target_predeps +QMAKE_EXTRA_COMPILERS += dictoolbuild diff --git a/examples/webenginewidgets/spellchecker/webview.cpp b/examples/webenginewidgets/spellchecker/webview.cpp new file mode 100644 index 000000000..6c73d88fa --- /dev/null +++ b/examples/webenginewidgets/spellchecker/webview.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "webview.h" +#include <QContextMenuEvent> +#include <QMenu> +#include <QWebEngineProfile> +#include <QWebEngineContextMenuData> + +WebView::WebView(QWidget *parent) + : QWebEngineView(parent) +{ + m_spellCheckLanguages["English"] = "en-US"; + m_spellCheckLanguages["German"] = "de-DE"; + QWebEngineProfile *profile = page()->profile(); + profile->setSpellCheckEnabled(true); + profile->setSpellCheckLanguage("en-US"); +} + +void WebView::contextMenuEvent(QContextMenuEvent *event) +{ + const QWebEngineContextMenuData &data = page()->contextMenuData(); + Q_ASSERT(data.isValid()); + + if (!data.isContentEditable()) { + QWebEngineView::contextMenuEvent(event); + return; + } + + QWebEngineProfile *profile = page()->profile(); + const QString &language = profile->spellCheckLanguage(); + QMenu *menu = page()->createStandardContextMenu(); + menu->addSeparator(); + + QAction *spellcheckAction = new QAction(tr("Check Spelling")); + spellcheckAction->setCheckable(true); + spellcheckAction->setChecked(profile->isSpellCheckEnabled()); + connect(spellcheckAction, &QAction::toggled, this, [profile](bool toogled) { + profile->setSpellCheckEnabled(toogled); + }); + menu->addAction(spellcheckAction); + + if (profile->isSpellCheckEnabled()) { + QMenu *subMenu = menu->addMenu(tr("Select Language")); + foreach (const QString &str, m_spellCheckLanguages.keys()) { + QAction *action = subMenu->addAction(str); + action->setCheckable(true); + QString lang = m_spellCheckLanguages[str]; + action->setChecked(language == lang); + connect(action, &QAction::triggered, this, [profile, lang](){ + profile->setSpellCheckLanguage(lang); + }); + } + } + connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater); + menu->popup(event->globalPos()); +} + + diff --git a/examples/webenginewidgets/spellchecker/webview.h b/examples/webenginewidgets/spellchecker/webview.h new file mode 100644 index 000000000..787d06a1a --- /dev/null +++ b/examples/webenginewidgets/spellchecker/webview.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef WEBVIEW_H +#define WEBVIEW_H + +#include <QWebEngineView> + +class WebView : public QWebEngineView +{ + Q_OBJECT + +public: + WebView(QWidget *parent = nullptr); + +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + +private: + QMap<QString,QString> m_spellCheckLanguages; +}; + +#endif diff --git a/src/webengine/doc/qtwebengine.qdocconf b/src/webengine/doc/qtwebengine.qdocconf index 009902080..5e738f98d 100644 --- a/src/webengine/doc/qtwebengine.qdocconf +++ b/src/webengine/doc/qtwebengine.qdocconf @@ -66,6 +66,7 @@ exampledirs += . \ ../../core/doc/snippets \ ../../webenginewidgets/doc/snippets +examples.fileextensions += *.aff *.dic imagedirs += images diff --git a/src/webengine/doc/src/external-resources.qdoc b/src/webengine/doc/src/external-resources.qdoc index 8933513a4..2ef88e8a4 100644 --- a/src/webengine/doc/src/external-resources.qdoc +++ b/src/webengine/doc/src/external-resources.qdoc @@ -90,6 +90,11 @@ \title Shaka Player */ +/*! + \externalpage https://sourceforge.net/projects/hunspell/ + \title Hunspell Project +*/ + /* This prevents autolinking of each occurrence of 'WebEngine' To link to the WebEngine QML type, use explicit linking: -- GitLab