[evolution/wip/mcrha/webkit-jsc-api] Implement and use EvoEditor.SetFontName()
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution/wip/mcrha/webkit-jsc-api] Implement and use EvoEditor.SetFontName()
- Date: Mon, 25 Nov 2019 17:42:19 +0000 (UTC)
commit 65a28ff08b28ed08023f80941bbbedcd5d5b2e2e
Author: Milan Crha <mcrha redhat com>
Date: Mon Nov 25 18:43:28 2019 +0100
Implement and use EvoEditor.SetFontName()
data/webkit/e-editor.js | 205 ++++++++++++++++++++++------
data/webkit/e-selection.js | 82 +++++++++++
data/webkit/e-undo-redo.js | 38 ++++++
src/modules/webkit-editor/e-webkit-editor.c | 18 ++-
4 files changed, 297 insertions(+), 46 deletions(-)
---
diff --git a/data/webkit/e-editor.js b/data/webkit/e-editor.js
index 55a3b0c2c8..7c615d219c 100644
--- a/data/webkit/e-editor.js
+++ b/data/webkit/e-editor.js
@@ -68,6 +68,7 @@ var EvoEditor = {
mode : 1, // one of the MODE constants
storedSelection : null,
inheritThemeColors : false,
+ checkInheritFontsOnChange : false,
forceFormatStateUpdate : false,
formattingState : {
mode : -1,
@@ -892,28 +893,16 @@ EvoEditor.SetBlockFormat = function(format)
createParent : null,
firstLI : true,
targetElement : null,
- selectionBaseNode : null,
- selectionBaseOffset : -1,
- selectionExtentNode : null,
- selectionExtentOffset : -1,
+ selectionUpdater : null,
flat : true,
onlyBlockElements : true,
exec : function(parent, element) {
- var newElement, changeBase = false, changeExtent = false;
+ var newElement;
- if (traversar.selectionBaseNode) {
- changeBase = element === traversar.selectionBaseNode ||
- (traversar.selectionBaseNode.noteType ==
traversar.selectionBaseNode.TEXT_NODE &&
- traversar.selectionBaseNode.parentElement === element);
- }
-
- if (traversar.selectionExtentNode) {
- changeExtent = element === traversar.selectionExtentNode ||
- (traversar.selectionExtentNode.noteType ==
traversar.selectionExtentNode.TEXT_NODE &&
- traversar.selectionExtentNode.parentElement === element);
- }
+ if (traversar.selectionUpdater)
+ traversar.selectionUpdater.beforeRemove(element);
if (traversar.firstLI) {
if (traversar.createParent) {
@@ -925,16 +914,15 @@ EvoEditor.SetBlockFormat = function(format)
newElement = EvoEditor.renameElement(element, traversar.toSet.tagName,
traversar.toSet.attributes, traversar.targetElement);
- if (changeBase)
- traversar.selectionBaseNode = newElement;
-
- if (changeExtent)
- traversar.selectionExtentNode = newElement;
+ if (traversar.selectionUpdater)
+ traversar.selectionUpdater.afterRemove(newElement);
return true;
}
};
+ traversar.selectionUpdater = EvoSelection.CreateUpdaterObject();
+
var affected = EvoEditor.ClaimAffectedContent(null, null,
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE);
switch (format) {
@@ -985,36 +973,13 @@ EvoEditor.SetBlockFormat = function(format)
throw "EvoEditor.SetBlockFormat: Unknown block format value: '" + format + "'";
}
- var selectionBefore = EvoSelection.Store(document);
-
EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBlockFormat", null, null,
EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
- traversar.selectionBaseNode = document.getSelection().baseNode;
- traversar.selectionBaseOffset = document.getSelection().baseOffset;
- if (!document.getSelection().isCollapsed) {
- traversar.selectionExtentNode = document.getSelection().extentNode;
- traversar.selectionExtentOffset = document.getSelection().extentOffset;
- }
-
try {
EvoEditor.ForeachChildInAffectedContent(affected, traversar);
- if (traversar.selectionBaseNode && traversar.selectionBaseNode.parentElement) {
- var selection = {
- baseElem : EvoSelection.GetChildPath(document.body,
traversar.selectionBaseNode),
- baseOffset : traversar.selectionBaseOffset
- };
-
- if (traversar.selectionExtentNode) {
- selection.extentElem = EvoSelection.GetChildPath(document.body,
traversar.selectionExtentNode);
- selection.extentOffset = traversar.selectionExtentOffset;
- }
-
- EvoSelection.Restore(document, selection);
- } else {
- EvoSelection.Restore(document, selectionBefore);
- }
+ traversar.selectionUpdater.restore();
} finally {
EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "setBlockFormat");
EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
@@ -1316,6 +1281,151 @@ EvoEditor.SetMode = function(mode)
}
}
+EvoEditor.applyFontReset = function(record, isUndo)
+{
+ if (record.changes) {
+ var ii;
+
+ if (isUndo) {
+ for (ii = record.changes.length - 1; ii >= 0; ii--) {
+ var change = record.changes[ii];
+ var parent = EvoSelection.FindElementByPath(document.body, change.parentPath);
+
+ if (!parent) {
+ throw "EvoEditor.applyFontReset: Cannot find node at path " +
change.path;
+ }
+
+ parent.innerHTML = change.htmlBefore;
+ }
+ } else {
+ for (ii = 0; ii < record.changes.length; ii++) {
+ var change = record.changes[ii];
+ var parent = EvoSelection.FindElementByPath(document.body, change.parentPath);
+
+ if (!parent) {
+ throw "EvoEditor.applyFontReset: Cannot find node at path " +
change.path;
+ }
+
+ parent.innerHTML = change.htmlAfter;
+ }
+ }
+ }
+}
+
+EvoEditor.replaceInheritFonts = function(undoRedoRecord, selectionUpdater)
+{
+ var nodes, ii;
+
+ nodes = document.querySelectorAll("font[face=inherit]");
+
+ for (ii = nodes.length - 1; ii >= 0; ii--) {
+ var node = nodes.item(ii);
+
+ if (!node || (!undoRedoRecord && !document.getSelection().containsNode(node, true)))
+ continue;
+
+ var parent, change = null;
+
+ parent = node.parentElement;
+
+ if (undoRedoRecord) {
+ if (!undoRedoRecord.changes)
+ undoRedoRecord.changes = [];
+
+ change = {
+ parentPath : EvoSelection.GetChildPath(document.body, parent),
+ htmlBefore : parent.innerHTML,
+ htmlAfter : ""
+ };
+
+ undoRedoRecord.changes[undoRedoRecord.changes.length] = change;
+ }
+
+ if (node.attributes.length == 1) {
+ var child;
+
+ while (node.firstChild) {
+ var child = node.firstChild;
+
+ selectionUpdater.beforeRemove(child);
+
+ parent.insertBefore(child, node);
+
+ selectionUpdater.afterRemove(child);
+ }
+
+ parent.removeChild(node);
+ } else {
+ node.removeAttribute("face");
+ }
+
+ if (change)
+ change.htmlAfter = parent.innerHTML;
+ }
+
+ if (undoRedoRecord && undoRedoRecord.changes)
+ undoRedoRecord.apply = EvoEditor.applyFontReset;
+}
+
+EvoEditor.maybeReplaceInheritFonts = function()
+{
+ if (document.querySelectorAll("font[face=inherit]").length <= 0)
+ return;
+
+ var record, selectionUpdater;
+
+ selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+ record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "UnsetFontName", null, null,
EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+ try {
+ EvoEditor.replaceInheritFonts(record, selectionUpdater);
+
+ selectionUpdater.restore();
+ } finally {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "UnsetFontName");
+
+ if (record)
+ EvoUndoRedo.GroupTopRecords(2);
+ }
+}
+
+EvoEditor.SetFontName = function(name)
+{
+ if (!name || name == "")
+ name = "inherit";
+
+ var record, selectionUpdater = EvoSelection.CreateUpdaterObject();
+
+ record = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName", null, null,
+ EvoEditor.CLAIM_CONTENT_FLAG_USE_PARENT_BLOCK_NODE | EvoEditor.CLAIM_CONTENT_FLAG_SAVE_HTML);
+ try {
+ document.execCommand("FontName", false, name);
+
+ if (document.getSelection().isCollapsed) {
+ if (name == "inherit")
+ EvoEditor.checkInheritFontsOnChange = true;
+
+ /* Format change on collapsed selection is not applied immediately */
+ if (record)
+ record.ignore = true;
+ } else if (name == "inherit") {
+ var subrecord;
+
+ subrecord = EvoUndoRedo.StartRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName",
null, null, EvoEditor.CLAIM_CONTENT_FLAG_NONE);
+ try {
+ EvoEditor.replaceInheritFonts(subrecord, selectionUpdater);
+ selectionUpdater.restore();
+ } finally {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_CUSTOM, "SetFontName");
+ }
+ }
+ } finally {
+ EvoUndoRedo.StopRecord(EvoUndoRedo.RECORD_KIND_GROUP, "SetFontName");
+ EvoEditor.maybeUpdateFormattingState(EvoEditor.FORCE_MAYBE);
+ EvoEditor.EmitContentChanged();
+ }
+}
+
EvoEditor.convertHtmlToSend = function()
{
var html, bgcolor, text, link, vlink;
@@ -1554,6 +1664,11 @@ EvoEditor.UpdateThemeStyleSheet = function(css)
document.onload = EvoEditor.initializeContent;
document.onselectionchange = function() {
+ if (EvoEditor.checkInheritFontsOnChange) {
+ EvoEditor.checkInheritFontsOnChange = false;
+ EvoEditor.maybeReplaceInheritFonts();
+ }
+
EvoEditor.maybeUpdateFormattingState(EvoEditor.forceFormatStateUpdate ? EvoEditor.FORCE_YES :
EvoEditor.FORCE_MAYBE);
EvoEditor.forceFormatStateUpdate = false;
};
diff --git a/data/webkit/e-selection.js b/data/webkit/e-selection.js
index e56e45fcf6..5513e63f0c 100644
--- a/data/webkit/e-selection.js
+++ b/data/webkit/e-selection.js
@@ -309,3 +309,85 @@ EvoSelection.FromString = function(str)
return selection;
}
+
+/* The so-called updater object has several methods to work with when removing
+ elements from the structure, which tries to preserve selection in the new
+ document structure. The methods are:
+
+ beforeRemove(node) - called before going to remove the 'node'
+ afterRemove(newNode) - with what the 'node' from beforeRemove() had been replaced
+ restore() - called at the end, to restore the selection
+ */
+EvoSelection.CreateUpdaterObject = function()
+{
+ var obj = {
+ selectionBefore : null,
+ selectionBaseNode : null,
+ selectionBaseOffset : -1,
+ selectionExtentNode : null,
+ selectionExtentOffset : -1,
+ changeBase : false,
+ changeExtent : false,
+
+ beforeRemove : function(node) {
+ this.changeBase = false;
+ this.changeExtent = false;
+
+ if (this.selectionBaseNode) {
+ this.changeBase = node === this.selectionBaseNode ||
+ (this.selectionBaseNode.noteType == this.selectionBaseNode.TEXT_NODE
&&
+ this.selectionBaseNode.parentElement === node);
+ }
+
+ if (this.selectionExtentNode) {
+ this.changeExtent = node === this.selectionExtentNode ||
+ (this.selectionExtentNode.noteType ==
this.selectionExtentNode.TEXT_NODE &&
+ this.selectionExtentNode.parentElement === node);
+ }
+ },
+
+ afterRemove : function(newNode) {
+ if (this.changeBase) {
+ this.selectionBaseNode = newNode;
+ this.selectionBaseOffset += EvoSelection.GetOverallTextOffset(newNode);
+ }
+
+ if (this.changeExtent) {
+ this.selectionExtentNode = newNode;
+ this.selectionExtentOffset += EvoSelection.GetOverallTextOffset(newNode);
+ }
+
+ this.changeBase = false;
+ this.changeExtent = false;
+ },
+
+ restore : function() {
+ if (this.selectionBaseNode && this.selectionBaseNode.parentElement) {
+ var selection = {
+ baseElem : EvoSelection.GetChildPath(document.body,
this.selectionBaseNode),
+ baseOffset : this.selectionBaseOffset
+ };
+
+ if (this.selectionExtentNode) {
+ selection.extentElem = EvoSelection.GetChildPath(document.body,
this.selectionExtentNode);
+ selection.extentOffset = this.selectionExtentOffset;
+ }
+
+ EvoSelection.Restore(document, selection);
+ } else {
+ EvoSelection.Restore(document, this.selectionBefore);
+ }
+ }
+ };
+
+ obj.selectionBefore = EvoSelection.Store(document);
+ obj.selectionBaseNode = document.getSelection().baseNode;
+ obj.selectionBaseOffset = document.getSelection().baseOffset;
+
+ if (!document.getSelection().isCollapsed) {
+ obj.selectionExtentNode = document.getSelection().extentNode;
+ obj.selectionExtentOffset = document.getSelection().extentOffset;
+ }
+
+ return obj;
+}
diff --git a/data/webkit/e-undo-redo.js b/data/webkit/e-undo-redo.js
index 454f865b3a..3a18649762 100644
--- a/data/webkit/e-undo-redo.js
+++ b/data/webkit/e-undo-redo.js
@@ -751,4 +751,42 @@ EvoUndoRedo.Clear = function()
EvoUndoRedo.stack.clear();
}
+EvoUndoRedo.GroupTopRecords = function(nRecords, opType)
+{
+ if (EvoUndoRedo.ongoingRecordings.length)
+ throw "EvoUndoRedo.GroupTopRecords: Cannot be called when there are ongoing recordings";
+
+ var group = {};
+
+ group.kind = EvoUndoRedo.RECORD_KIND_GROUP;
+ group.opType = opType;
+ group.selectionBefore = null;
+ group.selectionAfter = null;
+ group.records = [];
+
+ while (nRecords >= 1) {
+ nRecords--;
+
+ var record = EvoUndoRedo.stack.undo();
+
+ if (!record)
+ break;
+
+ group.records[group.records.length] = record;
+ group.selectionBefore = record.selectionBefore;
+
+ if (!group.selectionAfter)
+ group.selectionAfter = record.selectionAfter;
+
+ if (!group.opType)
+ group.opType = record.opType + "::Grouped";
+ }
+
+ if (group.records.length) {
+ group.records = group.records.reverse();
+
+ EvoUndoRedo.stack.push(group);
+ }
+}
+
EvoUndoRedo.Attach();
diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c
index 2207c3e690..f2510283d7 100644
--- a/src/modules/webkit-editor/e-webkit-editor.c
+++ b/src/modules/webkit-editor/e-webkit-editor.c
@@ -602,6 +602,20 @@ formatting_changed_cb (WebKitUserContentManager *manager,
}
g_clear_object (&jsc_value);
+ changed = FALSE;
+ jsc_value = jsc_value_object_get_property (jsc_params, "fontFamily");
+ if (jsc_value && jsc_value_is_string (jsc_value)) {
+ gchar *value = jsc_value_to_string (jsc_value);
+
+ if (g_strcmp0 (value, wk_editor->priv->font_name) != 0) {
+ update_style_flag (E_WEBKIT_EDITOR_STYLE_IS_MONOSPACE, g_strcmp0 (value, "monospace")
== 0);
+ changed = TRUE;
+ }
+
+ g_free (value);
+ }
+ g_clear_object (&jsc_value);
+
wk_editor->priv->temporary_style_flags = wk_editor->priv->style_flags;
#undef update_style_flag
@@ -3862,7 +3876,9 @@ webkit_editor_set_style_flag (EWebKitEditor *wk_editor,
webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Strikethrough");
break;
case E_WEBKIT_EDITOR_STYLE_IS_MONOSPACE:
- webkit_web_view_execute_editing_command_with_argument (WEBKIT_WEB_VIEW (wk_editor),
"FontName", "monospace");
+ e_web_view_jsc_run_script (WEBKIT_WEB_VIEW (wk_editor), wk_editor->priv->cancellable,
+ "EvoEditor.SetFontName(%s);",
+ do_set ? "monospace" : "");
break;
case E_WEBKIT_EDITOR_STYLE_IS_SUBSCRIPT:
webkit_web_view_execute_editing_command (WEBKIT_WEB_VIEW (wk_editor), "Subscript");
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]