From 67d81c3e15cbffc51e8dc5e2fd4a4e2eb5c14285 Mon Sep 17 00:00:00 2001 From: Michael James Gratton Date: Wed, 4 Jan 2017 20:11:01 +1100 Subject: [PATCH] Re-implement composer font family/size action state updating. * src/client/composer/composer-web-view.vala (ComposerWebView): Add new ::cursor_style_changed signal, hook it up to the JS cursorStyleChanged message and interpret the raw results from the web view before passing it on. * src/client/composer/composer-widget.vala (ComposerWidget): Replace remainder of old ::update_actions method with a listener for ComposerWebView::cursor_style_changed. Reimplement in terms of that signal. * ui/composer-web-view.js: Keep track of font family and size changes, send a cursorStyleChanged message when they change. --- src/client/composer/composer-web-view.vala | 58 ++++++++++++++ src/client/composer/composer-widget.vala | 91 +++++----------------- ui/composer-web-view.js | 27 +++++++ 3 files changed, 106 insertions(+), 70 deletions(-) diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala index bfba08b0..b470c591 100644 --- a/src/client/composer/composer-web-view.vala +++ b/src/client/composer/composer-web-view.vala @@ -13,6 +13,17 @@ public class ComposerWebView : ClientWebView { private const string COMMAND_STACK_CHANGED = "commandStackChanged"; + private const string CURSOR_STYLE_CHANGED = "cursorStyleChanged"; + + private const string[] SANS_FAMILY_NAMES = { + "sans", "arial", "trebuchet", "helvetica" + }; + private const string[] SERIF_FAMILY_NAMES = { + "serif", "georgia", "times" + }; + private const string[] MONO_FAMILY_NAMES = { + "monospace", "courier", "console" + }; private const string HTML_BODY = """ @@ -61,6 +72,21 @@ public class ComposerWebView : ClientWebView { """; private const string CURSOR = ""; + private static Gee.HashMap font_family_map = + new Gee.HashMap(); + + static construct { + foreach (string name in SANS_FAMILY_NAMES) { + font_family_map["sans"] = name; + } + foreach (string name in SERIF_FAMILY_NAMES) { + font_family_map["serif"] = name; + } + foreach (string name in MONO_FAMILY_NAMES) { + font_family_map["monospace"] = name; + } + } + private static WebKit.UserScript? app_script = null; public static void load_resources() @@ -82,6 +108,10 @@ public class ComposerWebView : ClientWebView { /** Emitted when the web view's undo/redo stack has changed. */ public signal void command_stack_changed(bool can_undo, bool can_redo); + /** Emitted when the style under the cursor has changed. */ + public signal void cursor_style_changed(string face, uint size); + + public ComposerWebView(Configuration config) { base(config); this.user_content_manager.add_script(ComposerWebView.app_script); @@ -100,7 +130,12 @@ public class ComposerWebView : ClientWebView { } }); result.unref(); + this.user_content_manager.script_message_received[CURSOR_STYLE_CHANGED].connect( + on_cursor_style_changed_message + ); + register_message_handler(COMMAND_STACK_CHANGED); + register_message_handler(CURSOR_STYLE_CHANGED); } /** @@ -353,6 +388,29 @@ public class ComposerWebView : ClientWebView { return false; } + private void on_cursor_style_changed_message(WebKit.JavascriptResult result) { + try { + string[] values = WebKitUtil.to_string(result).split(","); + string view_name = values[0].down(); + string? font_family = "sans"; + foreach (string name in ComposerWebView.font_family_map.keys) { + if (name in view_name) { + font_family = ComposerWebView.font_family_map[name]; + break; + } + } + + uint font_size = 12; + values[1].scanf("%dpx", out font_size); + + cursor_style_changed(font_family, font_size); + } catch (Geary.JS.Error err) { + debug("Could not get cursor style: %s", err.message); + } finally { + result.unref(); + } + } + // We really want to examine // Gdk.Keymap.get_default().get_modifier_state(), instead of // storing whether the shift key is down at each keypress, but it diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 6c536580..64b76ec9 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -12,6 +12,8 @@ private errordomain AttachmentError { // The actual widget for sending messages. Should be put in a ComposerContainer [GtkTemplate (ui = "/org/gnome/Geary/composer-widget.ui")] public class ComposerWidget : Gtk.EventBox { + + public enum ComposeType { NEW_MESSAGE, REPLY, @@ -475,8 +477,8 @@ public class ComposerWidget : Gtk.EventBox { this.editor.load_changed.connect(on_load_changed); this.editor.mouse_target_changed.connect(on_mouse_target_changed); this.editor.get_editor_state().notify["typing-attributes"].connect(on_typing_attributes_changed); - // this.editor.move_focus.connect(update_actions); this.editor.selection_changed.connect(on_selection_changed); + this.editor.cursor_style_changed.connect(on_cursor_style_changed); this.editor.key_press_event.connect(on_editor_key_press); //this.editor.user_changed_contents.connect(reset_draft_timer); @@ -558,7 +560,6 @@ public class ComposerWidget : Gtk.EventBox { insert_action_group("cmp", this.actions); this.header.insert_action_group("cmh", this.actions); - update_actions(); get_action(ACTION_UNDO).set_enabled(false); get_action(ACTION_REDO).set_enabled(false); } @@ -799,11 +800,12 @@ public class ComposerWidget : Gtk.EventBox { // This is safe to call even when this connection hasn't been made. realize.disconnect(on_load_finished_and_realized); - update_actions(); - - this.actions.change_action_state(ACTION_SHOW_EXTENDED, false); - this.actions.change_action_state(ACTION_COMPOSE_AS_HTML, - GearyApplication.instance.config.compose_as_html); + this.actions.change_action_state( + ACTION_SHOW_EXTENDED, false + ); + this.actions.change_action_state( + ACTION_COMPOSE_AS_HTML, this.config.compose_as_html + ); if (can_delete_quote) this.editor.selection_changed.connect( @@ -1847,8 +1849,6 @@ public class ComposerWidget : Gtk.EventBox { // This overrides the keypress handling for the *widget*; the WebView editor's keypress overrides // are handled by on_editor_key_press public override bool key_press_event(Gdk.EventKey event) { - update_actions(); - switch (Gdk.keyval_name(event.keyval)) { case "Return": case "KP_Enter": @@ -1860,7 +1860,7 @@ public class ComposerWidget : Gtk.EventBox { } break; } - + return base.key_press_event(event); } @@ -2013,66 +2013,6 @@ public class ComposerWidget : Gtk.EventBox { return this.actions.lookup_action(action_name) as SimpleAction; } - /** - * Updates the states of the composer's actions and whether they should be enabled. - */ - private void update_actions() { - // Basic editor commands - get_action(ACTION_UNDO).set_enabled(this.editor.can_undo()); - get_action(ACTION_REDO).set_enabled(this.editor.can_redo()); - get_action(ACTION_CUT).set_enabled(this.editor.can_cut_clipboard()); - get_action(ACTION_COPY).set_enabled(this.editor.can_copy_clipboard()); - get_action(ACTION_PASTE).set_enabled(this.editor.can_paste_clipboard()); - get_action(ACTION_PASTE_WITH_FORMATTING).set_enabled( - this.editor.can_paste_clipboard() && - this.editor.is_rich_text - ); - - // // Style formatting actions. - // WebKit.DOM.Document document = this.editor.get_dom_document(); - // WebKit.DOM.DOMWindow window = document.get_default_view(); - // WebKit.DOM.DOMSelection? selection = window.get_selection(); - // if (selection == null) - // return; - - // get_action(ACTION_REMOVE_FORMAT).set_enabled(!selection.is_collapsed - // && this.editor.is_rich_text); - - // WebKit.DOM.Element? active = selection.focus_node as WebKit.DOM.Element; - // if (active == null && selection.focus_node != null) - // active = selection.focus_node.get_parent_element(); - - // if (active != null) { - // WebKit.DOM.CSSStyleDeclaration styles = window.get_computed_style(active, ""); - - // // Font family. - // string font_name = styles.get_property_value("font-family").down(); - // if (font_name.contains("sans") || - // font_name.contains("arial") || - // font_name.contains("trebuchet") || - // font_name.contains("helvetica")) - // this.actions.change_action_state(ACTION_FONT_FAMILY, "sans"); - // else if (font_name.contains("serif") || - // font_name.contains("georgia") || - // font_name.contains("times")) - // this.actions.change_action_state(ACTION_FONT_FAMILY, "serif"); - // else if (font_name.contains("monospace") || - // font_name.contains("courier") || - // font_name.contains("console")) - // this.actions.change_action_state(ACTION_FONT_FAMILY, "monospace"); - - // // Font size. - // int font_size; - // styles.get_property_value("font-size").scanf("%dpx", out font_size); - // if (font_size < 11) - // this.actions.change_action_state(ACTION_FONT_SIZE, "small"); - // else if (font_size > 20) - // this.actions.change_action_state(ACTION_FONT_SIZE, "large"); - // else - // this.actions.change_action_state(ACTION_FONT_SIZE, "medium"); - // } - } - private bool add_account_emails_to_from_list(Geary.Account other_account, bool set_active = false) { Geary.RFC822.MailboxAddresses primary_address = new Geary.RFC822.MailboxAddresses.single( other_account.information.primary_mailbox); @@ -2246,6 +2186,17 @@ public class ComposerWidget : Gtk.EventBox { ); } + private void on_cursor_style_changed(string font_family, uint font_size) { + this.actions.change_action_state(ACTION_FONT_FAMILY, font_family); + + if (font_size < 11) + this.actions.change_action_state(ACTION_FONT_SIZE, "small"); + else if (font_size > 20) + this.actions.change_action_state(ACTION_FONT_SIZE, "large"); + else + this.actions.change_action_state(ACTION_FONT_SIZE, "medium"); + } + private void on_typing_attributes_changed() { uint mask = this.editor.get_editor_state().get_typing_attributes(); this.actions.change_action_state( diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js index 62a648d3..e54dccef 100644 --- a/ui/composer-web-view.js +++ b/ui/composer-web-view.js @@ -27,6 +27,9 @@ ComposerPageState.prototype = { this.undoEnabled = false; this.redoEnabled = false; + this.cursorFontFamily = null; + this.cursorFontSize = null; + let state = this; document.addEventListener("click", function(e) { @@ -142,6 +145,30 @@ ComposerPageState.prototype = { }, linkClicked: function(element) { window.getSelection().selectAllChildren(element); + }, + selectionChanged: function() { + PageState.prototype.selectionChanged.apply(this, []); + + let selection = window.getSelection(); + let active = selection.focusNode; + if (active != null && active.nodeType != Node.ELEMENT_TYPE) { + active = active.parentNode; + } + + if (active != null) { + let styles = window.getComputedStyle(active); + let fontFamily = styles.getPropertyValue("font-family"); + let fontSize = styles.getPropertyValue("font-size"); + + if (fontFamily != this.cursorFontFamily || + fontSize != this.cursorFontSize) { + this.cursorFontFamily = fontFamily; + this.cursorFontSize = fontSize; + window.webkit.messageHandlers.cursorStyleChanged.postMessage( + fontFamily + "," + fontSize + ); + } + } } };