You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@corinthia.apache.org by pm...@apache.org on 2015/06/07 04:40:08 UTC

incubator-corinthia git commit: Cursor placement and selection (mouse only)

Repository: incubator-corinthia
Updated Branches:
  refs/heads/master 2edf0efda -> 783ebdcd2


Cursor placement and selection (mouse only)

Respond to mouse events in Editor, setting the cursor or selection as
appropriate.

To display the cursor, we use a Qt widget which is overlaid on top of
the web view. This is a custom class that simply fills its rectangle
with blue. It his hidden when there is a selection, and visible when
there is no selection.

The logic for rendering the selection is already implemented in
javascript code; it bypasses the selection in WebKit due to bugs on
older verisons of iOS (at the time the code was written; not sure if
they're still present). To render a selection, the JS code creates
<span> elements with a class of "uxwrite-selection" (this should be
"corinthia-selection"), which has a light blue background. These spans
are temporary; when a file is saved (which isn't yet supported in the Qt
version), they will be removed.

>From the C++ side, we indicate to the JS code that the cursor or
selection should be adjusted by calling the dragSelectionBegin() and
dragSelectionUpdate() methods on the Selection module of the JS
interface. This causes the appropriate changes to be made to the DOM to
insert <span> elements if a selection is to be displayed, and the
relevant callback methods to be invoked which we use to position and
show/hide the cursor widget.

The EditorJSCallbacks::setCursor() method gets invoked when the JS code
wants us to display a cursor. This callback is triggered after executing
one of the above JS methods, from within the logic in JSInterface.cpp
which checks for any pending callback methods awaiting invocation after
each JS API call.

Note that the JS code considers a "cursor" simply to be an empty
selection - that is, one where the start and end positions are the same.
This is why the handling of both are intertwined.

This commit also amends the sample.html file to explicitly reference the
external stylesheet builtin.css, which is needed for the
uxwrite-selection class. This is done with the following line:

    <link rel="stylesheet" href="builtin.css">

The explicit <link> element is a temporary measure; once we add support
for loading arbitrary files, we will need to dynamically add this to the
DOM so the relevant built-in styles are always available.


Project: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/commit/783ebdcd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/tree/783ebdcd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-corinthia/diff/783ebdcd

Branch: refs/heads/master
Commit: 783ebdcd2863a741c3a61a9539012f4dfb24cce0
Parents: 2edf0ef
Author: Peter Kelly <pe...@uxproductivity.com>
Authored: Sun Jun 7 09:20:16 2015 +0700
Committer: Peter Kelly <pe...@uxproductivity.com>
Committed: Sun Jun 7 09:20:16 2015 +0700

----------------------------------------------------------------------
 consumers/corinthia/res/builtin.css |  36 +++++++++
 consumers/corinthia/res/sample.html |   1 +
 consumers/corinthia/src/Editor.cpp  | 131 +++++++++++++++++++++++--------
 consumers/corinthia/src/Editor.h    |  34 ++++++++
 4 files changed, 169 insertions(+), 33 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/783ebdcd/consumers/corinthia/res/builtin.css
----------------------------------------------------------------------
diff --git a/consumers/corinthia/res/builtin.css b/consumers/corinthia/res/builtin.css
new file mode 100644
index 0000000..fd6e6b6
--- /dev/null
+++ b/consumers/corinthia/res/builtin.css
@@ -0,0 +1,36 @@
+.uxwrite-autocorrect { background-color: #c0ffc0; }
+.uxwrite-selection { background-color: rgb(201,221,238); }
+.uxwrite-spelling { background-color: rgb(255,128,128); }
+.uxwrite-match { background-color: rgb(255,255,0); }
+
+span.footnote::before {
+    content: " [ ";
+    color: #157efb;
+    font-weight: bold;
+}
+
+span.footnote::after {
+    content: " ] ";
+    color: #157efb;
+    font-weight: bold;
+}
+
+span.footnote {
+    color: #606060;
+}
+
+span.endnote::before {
+    content: " [ ";
+    color: #ca1cff;
+    font-weight: bold;
+}
+
+span.endnote::after {
+    content: " ] ";
+    color: #ca1cff;
+    font-weight: bold;
+}
+
+span.endnote {
+    color: #606060;
+}

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/783ebdcd/consumers/corinthia/res/sample.html
----------------------------------------------------------------------
diff --git a/consumers/corinthia/res/sample.html b/consumers/corinthia/res/sample.html
index ef8be70..51d0b1a 100644
--- a/consumers/corinthia/res/sample.html
+++ b/consumers/corinthia/res/sample.html
@@ -4,6 +4,7 @@
 <head>
   <meta name="generator" content="UX Write 2.1.1 (build ad61a0a); iOS 8.2">
   <meta charset="utf-8">
+  <link rel="stylesheet" href="builtin.css">
   <style>
 body {
     font-family: Palatino;

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/783ebdcd/consumers/corinthia/src/Editor.cpp
----------------------------------------------------------------------
diff --git a/consumers/corinthia/src/Editor.cpp b/consumers/corinthia/src/Editor.cpp
index 01171c9..734edaf 100644
--- a/consumers/corinthia/src/Editor.cpp
+++ b/consumers/corinthia/src/Editor.cpp
@@ -25,6 +25,33 @@
 #include <QVBoxLayout>
 #include <QHBoxLayout>
 #include <QCoreApplication>
+#include <QMouseEvent>
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                             Cursor                                             //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+Cursor::Cursor(QWidget *parent)
+{
+}
+
+Cursor::~Cursor()
+{
+}
+
+void Cursor::paintEvent(QPaintEvent *event)
+{
+    QPainter painter(this);
+    painter.fillRect(0,0,size().width(),size().height(),QColor(0,0,255));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                        EditorJSCallbacks                                       //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
 
 class EditorJSCallbacks : public JSCallbacks
 {
@@ -127,17 +154,21 @@ void EditorJSCallbacks::outlineUpdated()
 void EditorJSCallbacks::setCursor(int x, int y, int width, int height)
 {
     qStdOut() << "CB setCursor " << x << " " << y << " " << width << " " << height << endl;
+    _editor->cursor()->setVisible(true);
+    _editor->cursor()->setGeometry(x,y,width,height);
 }
 
 void EditorJSCallbacks::setSelectionHandles(int x1, int y1, int height1, int x2, int y2, int height2)
 {
     qStdOut() << "CB setSelectionHandles " << x1 << " " << y1 << " " << height1 << " "
     << x2 << " " << y2 << " " << height2 << endl;
+    _editor->cursor()->setVisible(false);
 }
 
 void EditorJSCallbacks::setTableSelection(int x, int y, int width, int height)
 {
     qStdOut() << "CB setTableSelection" << x << " " << y << " " << width << " " << height << endl;
+    _editor->cursor()->setVisible(false);
 }
 
 void EditorJSCallbacks::setSelectionBounds(int left, int top, int right, int bottom)
@@ -160,6 +191,12 @@ void EditorJSCallbacks::error(const QString &message, const QString &operation)
     qStdOut() << "CB error \"" << message << "\" \"" << operation << "\"" << endl;
 }
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                        EditorJSEvaluator                                       //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 QString EditorJSEvaluator::evaluate(const QString &script)
 {
     QWebFrame *frame = _webView->page()->mainFrame();
@@ -170,39 +207,11 @@ QString EditorJSEvaluator::evaluate(const QString &script)
         return result.toString();
 }
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                             Editor                                             //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
 
 Editor::Editor(QWidget *parent, Qt::WindowFlags f) : QWidget(parent,f)
 {
@@ -210,9 +219,17 @@ Editor::Editor(QWidget *parent, Qt::WindowFlags f) : QWidget(parent,f)
     _callbacks = new EditorJSCallbacks(this);
     _evaluator = new EditorJSEvaluator(_webView,_callbacks);
     _js = new JSInterface(_evaluator);
+    _selecting = false;
+    _cursor = new Cursor(this);
+    _cursor->setHidden(true);
+
     QObject::connect(_webView,SIGNAL(loadFinished(bool)),this,SLOT(webViewloadFinished(bool)));
+    _webView->installEventFilter(this);
+
     QVBoxLayout *layout = new QVBoxLayout(this);
+    layout->setContentsMargins(0,0,0,0);
     layout->addWidget(_webView);
+    layout->addWidget(_cursor);
     setLayout(layout);
 }
 
@@ -265,3 +282,51 @@ void Editor::webViewloadFinished(bool ok)
 
     processCallbacks(_evaluator);
 }
+
+void Editor::mouseDoubleClickEvent(QMouseEvent *event)
+{
+}
+
+void Editor::mouseMoveEvent(QMouseEvent *event)
+{
+    if (_selecting) {
+        js()->selection.dragSelectionUpdate(event->x(),event->y(),false);
+    }
+}
+
+void Editor::mousePressEvent(QMouseEvent *event)
+{
+    if (event->buttons() & Qt::LeftButton) {
+        _selecting = true;
+        js()->selection.dragSelectionBegin(event->x(),event->y(),false);
+    }
+}
+
+void Editor::mouseReleaseEvent(QMouseEvent *event)
+{
+    _selecting = false;
+}
+
+void Editor::resizeEvent(QResizeEvent *event)
+{
+    QWidget::resizeEvent(event);
+    _js->viewport.setViewportWidth(size().width());
+}
+
+bool Editor::eventFilter(QObject *obj, QEvent *event)
+{
+    if (obj == _webView) {
+        switch (event->type()) {
+            case QEvent::MouseButtonDblClick:
+            case QEvent::MouseButtonPress:
+            case QEvent::MouseButtonRelease:
+            case QEvent::MouseMove:
+            case QEvent::MouseTrackingChange:
+                this->event(event);
+                return true;
+            default:
+                break;
+        }
+    }
+    return QWidget::eventFilter(obj,event);
+}

http://git-wip-us.apache.org/repos/asf/incubator-corinthia/blob/783ebdcd/consumers/corinthia/src/Editor.h
----------------------------------------------------------------------
diff --git a/consumers/corinthia/src/Editor.h b/consumers/corinthia/src/Editor.h
index d077ee4..074894a 100644
--- a/consumers/corinthia/src/Editor.h
+++ b/consumers/corinthia/src/Editor.h
@@ -23,6 +23,29 @@ class EditorJSEvaluator;
 class QWebView;
 class JSInterface;
 
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                             Cursor                                             //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class Cursor : public QWidget
+{
+    Q_OBJECT
+public:
+    Cursor(QWidget *parent = 0);
+    virtual ~Cursor();
+
+protected:
+    virtual void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//                                                                                                //
+//                                             Editor                                             //
+//                                                                                                //
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
 class Editor : public QWidget
 {
     Q_OBJECT
@@ -31,13 +54,24 @@ public:
     virtual ~Editor();
     QWebView *webView() const;
     JSInterface *js() const;
+    Cursor *cursor() const { return _cursor; }
 
 public slots:
     void webViewloadFinished(bool ok);
 
+protected:
+    virtual void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+    virtual void mouseMoveEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+    virtual void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+    virtual void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
+    virtual void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
+    virtual bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE;
+
 private:
     QWebView *_webView;
     EditorJSCallbacks *_callbacks;
     EditorJSEvaluator *_evaluator;
     JSInterface *_js;
+    bool _selecting;
+    Cursor *_cursor;
 };