From 2aff2694b25cf9d49d06846a0447b0d607d0bff8 Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 4 Jan 2020 02:09:38 -0600 Subject: [PATCH 1/7] composer: Move formatting buttons to new toolbar Splits the toolbar at the top of the composer into two action bars at the bottom. One is always visible and contains undo, redo, spellcheck, a menu, and a button to toggle the other action bar. The other action bar contains the formatting buttons. Also cleans up the margins around the fields at the top. Implemented according to the mockups at https://gitlab.gnome.org/Teams/Design/app-mockups/raw/master/mail/composer.png --- desktop/org.gnome.Geary.gschema.xml | 6 + .../application-configuration.vala | 6 + src/client/composer/composer-widget.vala | 54 +- ui/composer-widget.ui | 904 +++++++++--------- 4 files changed, 486 insertions(+), 484 deletions(-) diff --git a/desktop/org.gnome.Geary.gschema.xml b/desktop/org.gnome.Geary.gschema.xml index f0068798..cac7a4d0 100644 --- a/desktop/org.gnome.Geary.gschema.xml +++ b/desktop/org.gnome.Geary.gschema.xml @@ -45,6 +45,12 @@ True if the folder list Paned is in the horizontal orientation. + + true + Show/hide formatting toolbar + True if the formatting toolbar in the composer is shown. + + 250 Position of message list pane diff --git a/src/client/application/application-configuration.vala b/src/client/application/application-configuration.vala index e2cc8f7b..89db44cf 100644 --- a/src/client/application/application-configuration.vala +++ b/src/client/application/application-configuration.vala @@ -22,6 +22,7 @@ public class Application.Configuration : Geary.BaseObject { public const string FOLDER_LIST_PANE_POSITION_HORIZONTAL_KEY = "folder-list-pane-position-horizontal"; public const string FOLDER_LIST_PANE_POSITION_KEY = "folder-list-pane-position"; public const string FOLDER_LIST_PANE_POSITION_VERTICAL_KEY = "folder-list-pane-position-vertical"; + public const string FORMATTING_TOOLBAR_VISIBLE = "formatting-toolbar-visible"; public const string MESSAGES_PANE_POSITION_KEY = "messages-pane-position"; public const string SEARCH_STRATEGY_KEY = "search-strategy"; public const string SINGLE_KEY_SHORTCUTS = "single-key-shortcuts"; @@ -104,6 +105,11 @@ public class Application.Configuration : Geary.BaseObject { get { return settings.get_boolean(FOLDER_LIST_PANE_HORIZONTAL_KEY); } } + public bool formatting_toolbar_visible { + get { return settings.get_boolean(FORMATTING_TOOLBAR_VISIBLE); } + set { settings.set_boolean(FORMATTING_TOOLBAR_VISIBLE, value); } + } + public int messages_pane_position { get { return settings.get_int(MESSAGES_PANE_POSITION_KEY); } set { settings.set_int(MESSAGES_PANE_POSITION_KEY, value); } diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 9253eab4..0a76f90b 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -131,6 +131,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private const string ACTION_INSERT_LINK = "insert-link"; private const string ACTION_COMPOSE_AS_HTML = "compose-as-html"; private const string ACTION_SHOW_EXTENDED_HEADERS = "show-extended-headers"; + private const string ACTION_SHOW_FORMATTING = "show-formatting"; private const string ACTION_DISCARD = "discard"; private const string ACTION_DETACH = "detach"; private const string ACTION_SEND = "send"; @@ -186,6 +187,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { { ACTION_SELECT_DICTIONARY, on_select_dictionary }, { ACTION_SEND, on_send }, { ACTION_SHOW_EXTENDED_HEADERS, on_toggle_action, null, "false", on_show_extended_headers_toggled }, + { ACTION_SHOW_FORMATTING, on_toggle_action, null, "false", on_show_formatting }, }; public static void add_accelerators(Application.Client application) { @@ -310,6 +312,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { [GtkChild] private Gtk.Label from_label; + [GtkChild] private Gtk.Box from_row; [GtkChild] private Gtk.Label from_single; [GtkChild] @@ -347,8 +350,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private EmailEntry reply_to_entry; private Components.EntryUndo reply_to_undo; - [GtkChild] - private Gtk.Label subject_label; + [GtkChild] private Gtk.Box subject_row; [GtkChild] private Gtk.Entry subject_entry; private Components.EntryUndo subject_undo; @@ -371,17 +373,10 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private Gtk.Widget recipients; [GtkChild] private Gtk.Box header_area; - [GtkChild] - private Gtk.Box insert_buttons; - [GtkChild] - private Gtk.Box font_style_buttons; - [GtkChild] - private Gtk.Box list_buttons; + [GtkChild] private Gtk.Revealer formatting; [GtkChild] private Gtk.Button insert_link_button; [GtkChild] - private Gtk.Button remove_format_button; - [GtkChild] private Gtk.Button select_dictionary_button; [GtkChild] private Gtk.MenuButton menu_button; @@ -1150,6 +1145,11 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { ); } + this.composer_actions.change_action_state( + ACTION_SHOW_FORMATTING, + this.application.config.formatting_toolbar_visible + ); + get_action(Action.Edit.UNDO).set_enabled(false); get_action(Action.Edit.REDO).set_enabled(false); @@ -1435,20 +1435,17 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { case PresentationMode.DETACHED: case PresentationMode.PANED: this.recipients.set_visible(true); - this.subject_label.set_visible(true); - this.subject_entry.set_visible(true); + this.subject_row.visible = true; break; case PresentationMode.INLINE: this.recipients.set_visible(true); - this.subject_label.set_visible(false); - this.subject_entry.set_visible(false); + this.subject_row.visible = false; break; case PresentationMode.INLINE_COMPACT: this.recipients.set_visible(false); - this.subject_label.set_visible(false); - this.subject_entry.set_visible(false); + this.subject_row.visible = false; set_compact_header_recipients(); break; } @@ -2123,10 +2120,9 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { update_cursor_actions(); - this.insert_buttons.visible = compose_as_html; - this.font_style_buttons.visible = compose_as_html; - this.list_buttons.visible = compose_as_html; - this.remove_format_button.visible = compose_as_html; + var show_formatting = (SimpleAction) this.composer_actions.lookup_action(ACTION_SHOW_FORMATTING); + show_formatting.set_enabled(compose_as_html); + update_formatting_toolbar(); this.menu_button.menu_model = (compose_as_html) ? this.html_menu : this.plain_menu; @@ -2146,6 +2142,20 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { } } + private void update_formatting_toolbar() { + var show_formatting = (SimpleAction) this.composer_actions.lookup_action(ACTION_SHOW_FORMATTING); + var text_format = (SimpleAction) this.composer_actions.lookup_action(ACTION_TEXT_FORMAT); + this.formatting.reveal_child = text_format.get_state().get_string() == "html" && show_formatting.get_state().get_boolean(); + } + + private void on_show_formatting(SimpleAction? action, Variant? new_state) { + bool show_formatting = new_state.get_boolean(); + this.application.config.formatting_toolbar_visible = show_formatting; + action.set_state(new_state); + + update_formatting_toolbar(); + } + private void on_font_family(SimpleAction action, Variant? param) { this.editor.execute_editing_command_with_argument( "fontname", param.get_string() @@ -2392,7 +2402,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { // the from address had to be set private bool update_from_field() { this.from_multiple.changed.disconnect(on_from_changed); - this.from_single.visible = this.from_multiple.visible = this.from_label.visible = false; + this.from_single.visible = this.from_multiple.visible = this.from_row.visible = false; // Don't show in inline unless the current account has // multiple email accounts or aliases, since these will be replies to a @@ -2411,7 +2421,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { return false; } - this.from_label.visible = true; + this.from_row.visible = true; this.from_label.set_mnemonic_widget(this.from_multiple); // Composer label (with mnemonic underscore) for the account selector // when choosing what address to send a message from. diff --git a/ui/composer-widget.ui b/ui/composer-widget.ui index 57ee5157..2899c6af 100644 --- a/ui/composer-widget.ui +++ b/ui/composer-widget.ui @@ -14,7 +14,6 @@ True False vertical - 2 True @@ -48,7 +47,7 @@ 6 vertical - + True False 6 @@ -343,10 +342,11 @@ - + True False 6 + 6 True @@ -467,461 +467,7 @@ True False - - - True - False - 6 - 6 - 4 - 6 - - - True - False - - - True - True - False - False - Undo last edit - edt.undo - True - - - True - False - 16 - edit-undo-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Redo last edit - edt.redo - True - - - True - False - 16 - edit-redo-symbolic - - - - - False - True - 1 - - - - - - False - True - 0 - - - - - True - False - - - True - True - False - False - Bold text - edt.bold - True - - - True - False - 16 - format-text-bold-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Italic text - edt.italic - True - - - True - False - 16 - format-text-italic-symbolic - - - - - False - True - 1 - - - - - True - True - False - False - Underline text - edt.underline - True - - - True - False - 16 - format-text-underline-symbolic - - - - - False - True - 2 - - - - - True - True - False - False - Strikethrough text - edt.strikethrough - True - - - True - False - 16 - format-text-strikethrough-symbolic - - - - - False - True - 3 - - - - - - False - True - 1 - - - - - True - False - - - True - True - False - False - Insert bulleted list - edt.ulist - True - - - True - False - 16 - format-unordered-list-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Insert numbered list - edt.olist - True - - - True - False - 16 - format-ordered-list-symbolic - - - - - False - True - 1 - - - - - - False - True - 2 - - - - - True - False - - - True - True - False - False - Indent or quote text - edt.indent - True - - - True - False - 16 - format-indent-more-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Un-indent or unquote text - edt.outdent - True - - - True - False - 16 - format-indent-less-symbolic - - - - - False - True - 1 - - - - - - False - True - 3 - - - - - True - False - - - True - True - False - False - Insert or update text link - edt.insert-link - True - - - True - False - 16 - insert-link-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Insert an image - edt.insert-image - True - - - True - False - 16 - insert-image-symbolic - - - - - False - True - 1 - - - - - - False - True - 4 - - - - - True - True - False - False - Remove text formatting - edt.remove-format - True - - - True - False - 16 - format-text-remove-symbolic - - - - - False - True - 5 - - - - - True - True - False - False - Select spell checking languages - win.select-dictionary - True - - - True - False - 16 - accessories-dictionary-symbolic - - - - - False - True - 6 - - - - - True - False - False - False - - - - - - False - True - end - 7 - - - - - True - True - end - 6 - 0 - - - - False - True - end - 8 - - - - - 0 - 0 - - + vertical True @@ -1011,10 +557,444 @@ - - 0 - 1 - + + + + True + slide-up + + + True + + + True + False + + + True + True + False + False + Bold text + edt.bold + True + + + True + False + 16 + format-text-bold-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Italic text + edt.italic + True + + + True + False + 16 + format-text-italic-symbolic + + + + + False + True + 1 + + + + + True + True + False + False + Underline text + edt.underline + True + + + True + False + 16 + format-text-underline-symbolic + + + + + False + True + 2 + + + + + True + True + False + False + Strikethrough text + edt.strikethrough + True + + + True + False + 16 + format-text-strikethrough-symbolic + + + + + False + True + 3 + + + + + + + + True + False + + + True + True + False + False + Insert bulleted list + edt.ulist + True + + + True + False + 16 + format-unordered-list-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Insert numbered list + edt.olist + True + + + True + False + 16 + format-ordered-list-symbolic + + + + + False + True + 1 + + + + + + + + True + False + + + True + True + False + False + Indent or quote text + edt.indent + True + + + True + False + 16 + format-indent-more-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Un-indent or unquote text + edt.outdent + True + + + True + False + 16 + format-indent-less-symbolic + + + + + False + True + 1 + + + + + + + + True + False + + + True + True + False + False + Insert or update text link + edt.insert-link + True + + + True + False + 16 + insert-link-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Insert an image + edt.insert-image + True + + + True + False + 16 + insert-image-symbolic + + + + + False + True + 1 + + + + + + + + True + True + False + False + Remove text formatting + edt.remove-format + True + + + True + False + 16 + format-text-remove-symbolic + + + + + + + + + + + True + + + True + False + + + True + True + False + False + Undo last edit + edt.undo + True + + + True + False + 16 + edit-undo-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Redo last edit + edt.redo + True + + + True + False + 16 + edit-redo-symbolic + + + + + False + True + 1 + + + + + + + + True + True + end + 6 + 0 + + + + + + True + False + False + False + + + True + view-more-symbolic + + + + + end + + + + + True + False + False + False + win.show-formatting + Show formatting toolbar + + + True + format-text-italic-symbolic + + + + + end + + + + + True + True + False + False + Select spell checking languages + win.select-dictionary + True + + + True + False + 16 + tools-check-spelling-symbolic + + + + + end + + + From decd33d3554018eaecd3ff846801efb5e7764c4f Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 11 Jan 2020 19:22:12 -0600 Subject: [PATCH 2/7] composer: Ellipsize from fields Allow the from_multiple combobox to ellipsize if the name/address is too long to fit on the screen. --- src/client/application/application-main-window.vala | 2 +- src/client/composer/composer-widget.vala | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/client/application/application-main-window.vala b/src/client/application/application-main-window.vala index 97920ff6..253cde9f 100644 --- a/src/client/application/application-main-window.vala +++ b/src/client/application/application-main-window.vala @@ -1207,7 +1207,7 @@ public class Application.MainWindow : on_conversation_view_added ); - this.conversations_paned.pack2(this.conversation_viewer, true, true); + this.conversations_paned.pack2(this.conversation_viewer, true, false); // Main toolbar this.main_toolbar = new MainToolbar(config); diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index 0a76f90b..ec9e9211 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -605,6 +605,11 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { ); this.background_work_pulse.repetition = FOREVER; + // Set the from_multiple combo box to ellipsize. This can't be done + // from the .ui file. + var cells = this.from_multiple.get_cells(); + ((Gtk.CellRendererText) cells.data).ellipsize = END; + load_entry_completions(); } From aaf172b77f19d1b6b151c7f71c581211e2c3234d Mon Sep 17 00:00:00 2001 From: James Westman Date: Mon, 13 Jan 2020 20:28:49 -0600 Subject: [PATCH 3/7] composer: Wrap toolbar when it gets too narrow This way, the composer fits on even smaller screens than before. Works by simply listening to the size-allocate signal and changing the orientation of a box if the allocated width is less than or equal to the combined width of the two rows. --- src/client/composer/composer-widget.vala | 17 + ui/composer-widget.ui | 596 ++++++++++++----------- 2 files changed, 328 insertions(+), 285 deletions(-) diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index ec9e9211..b4668980 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -374,6 +374,9 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { [GtkChild] private Gtk.Box header_area; [GtkChild] private Gtk.Revealer formatting; + [GtkChild] private Gtk.Box toolbar_box; + [GtkChild] private Gtk.Box top_buttons; + [GtkChild] private Gtk.Box bottom_buttons; [GtkChild] private Gtk.Button insert_link_button; [GtkChild] @@ -2862,4 +2865,18 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { this.background_work_pulse.reset(); this.show_background_work_timeout.reset(); } + + [GtkCallback] + private void on_toolbar_size_allocate(Gtk.Widget widget, Gtk.Allocation rect) { + int top_width = this.top_buttons.get_allocated_width(); + int bottom_width = this.bottom_buttons.get_allocated_width(); + // add 6 for spacing + int width = top_width + bottom_width + 6; + + if (rect.width <= width) { + this.toolbar_box.orientation = VERTICAL; + } else { + this.toolbar_box.orientation = HORIZONTAL; + } + } } diff --git a/ui/composer-widget.ui b/ui/composer-widget.ui index 2899c6af..1288ac6e 100644 --- a/ui/composer-widget.ui +++ b/ui/composer-widget.ui @@ -566,296 +566,322 @@ True - + True False + horizontal + 6 + True + - - True - True - False - False - Bold text - edt.bold - True - - - True - False - 16 - format-text-bold-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Italic text - edt.italic - True - - - True - False - 16 - format-text-italic-symbolic - - - - - False - True - 1 - - - - - True - True - False - False - Underline text - edt.underline - True - - - True - False - 16 - format-text-underline-symbolic - - - - - False - True - 2 - - - - - True - True - False - False - Strikethrough text - edt.strikethrough - True - - - True - False - 16 - format-text-strikethrough-symbolic - - - - - False - True - 3 - - - - - - - - True - False - - - True - True - False - False - Insert bulleted list - edt.ulist - True - - - True - False - 16 - format-unordered-list-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Insert numbered list - edt.olist - True - - - True - False - 16 - format-ordered-list-symbolic - - - - - False - True - 1 - - - - - - - - True - False - - - True - True - False - False - Indent or quote text - edt.indent - True - - - True - False - 16 - format-indent-more-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Un-indent or unquote text - edt.outdent - True - - - True - False - 16 - format-indent-less-symbolic - - - - - False - True - 1 - - - - - - - - True - False - - - True - True - False - False - Insert or update text link - edt.insert-link - True - - - True - False - 16 - insert-link-symbolic - - - - - False - True - 0 - - - - - True - True - False - False - Insert an image - edt.insert-image - True - - - True - False - 16 - insert-image-symbolic - - - - - False - True - 1 - - - - - - - - True - True - False - False - Remove text formatting - edt.remove-format - True - - + True False - 16 - format-text-remove-symbolic + 6 + start + + + True + False + + + True + True + False + False + Bold text + edt.bold + True + + + True + False + 16 + format-text-bold-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Italic text + edt.italic + True + + + True + False + 16 + format-text-italic-symbolic + + + + + False + True + 1 + + + + + True + True + False + False + Underline text + edt.underline + True + + + True + False + 16 + format-text-underline-symbolic + + + + + False + True + 2 + + + + + True + True + False + False + Strikethrough text + edt.strikethrough + True + + + True + False + 16 + format-text-strikethrough-symbolic + + + + + False + True + 3 + + + + + + + + True + False + + + True + True + False + False + Insert bulleted list + edt.ulist + True + + + True + False + 16 + format-unordered-list-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Insert numbered list + edt.olist + True + + + True + False + 16 + format-ordered-list-symbolic + + + + + False + True + 1 + + + + + + + + True + False + + + True + True + False + False + Indent or quote text + edt.indent + True + + + True + False + 16 + format-indent-more-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Un-indent or unquote text + edt.outdent + True + + + True + False + 16 + format-indent-less-symbolic + + + + + False + True + 1 + + + + + + + + True + False + + + True + True + False + False + Insert or update text link + edt.insert-link + True + + + True + False + 16 + insert-link-symbolic + + + + + False + True + 0 + + + + + True + True + False + False + Insert an image + edt.insert-image + True + + + True + False + 16 + insert-image-symbolic + + + + + False + True + 1 + + + + + + + + + + True + False + 6 + start + + + True + True + False + False + Remove text formatting + edt.remove-format + True + + + True + False + 16 + format-text-remove-symbolic + + + + From 0eed1bb21aff8b66ada9316f00b8ad356dcbf3d3 Mon Sep 17 00:00:00 2001 From: James Westman Date: Tue, 14 Jan 2020 22:35:18 -0600 Subject: [PATCH 4/7] composer: Add font buttons to toolbar This replaces the menu options in the overflow menu with nicer looking, more discoverable toolbar buttons. They work much the same way as before. --- icons/font-color-symbolic.svg | 19 ++ icons/font-size-symbolic.svg | 19 ++ icons/meson.build | 2 + src/client/composer/composer-web-view.vala | 11 +- src/client/composer/composer-widget.vala | 67 ++++-- src/client/util/util-gtk.vala | 9 + .../composer/composer-web-view-test.vala | 19 +- test/js/composer-page-state-test.vala | 6 +- ui/composer-menus.ui | 58 ----- ui/composer-web-view.js | 9 +- ui/composer-widget.ui | 218 +++++++++++++++--- 11 files changed, 316 insertions(+), 121 deletions(-) create mode 100644 icons/font-color-symbolic.svg create mode 100644 icons/font-size-symbolic.svg diff --git a/icons/font-color-symbolic.svg b/icons/font-color-symbolic.svg new file mode 100644 index 00000000..c51bc931 --- /dev/null +++ b/icons/font-color-symbolic.svg @@ -0,0 +1,19 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/icons/font-size-symbolic.svg b/icons/font-size-symbolic.svg new file mode 100644 index 00000000..07ecc544 --- /dev/null +++ b/icons/font-size-symbolic.svg @@ -0,0 +1,19 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + \ No newline at end of file diff --git a/icons/meson.build b/icons/meson.build index 2234d3b0..138ed558 100644 --- a/icons/meson.build +++ b/icons/meson.build @@ -6,6 +6,8 @@ icon_files = files( 'detach-symbolic.svg', 'text-x-generic-symbolic.svg', 'edit-symbolic.svg', + 'font-size-symbolic.svg', + 'font-color-symbolic.svg', 'format-ordered-list-symbolic.svg', 'format-ordered-list-symbolic-rtl.svg', 'format-text-remove-symbolic.svg', diff --git a/src/client/composer/composer-web-view.vala b/src/client/composer/composer-web-view.vala index 7f73becd..3dabf7fe 100644 --- a/src/client/composer/composer-web-view.vala +++ b/src/client/composer/composer-web-view.vala @@ -55,11 +55,16 @@ public class Composer.WebView : Components.WebView { public string link_url { get; private set; default = ""; } public string font_family { get; private set; default = "sans"; } public uint font_size { get; private set; default = 12; } + public Gdk.RGBA font_color { + get; + private set; + default = Util.Gtk.rgba(0, 0, 0, 1); + } private uint context = 0; public EditContext(string message) { - string[] values = message.split(","); + string[] values = message.split(";"); this.context = (uint) uint64.parse(values[0]); this.link_url = values[1]; @@ -73,6 +78,10 @@ public class Composer.WebView : Components.WebView { } this.font_size = (uint) uint64.parse(values[3]); + + Gdk.RGBA font_color = {0, 0, 0, 0}; + font_color.parse(values[4]); + this.font_color = font_color; } } diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index b4668980..e09a8806 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -129,7 +129,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private const string ACTION_COLOR = "color"; private const string ACTION_INSERT_IMAGE = "insert-image"; private const string ACTION_INSERT_LINK = "insert-link"; - private const string ACTION_COMPOSE_AS_HTML = "compose-as-html"; + private const string ACTION_TEXT_FORMAT = "text-format"; private const string ACTION_SHOW_EXTENDED_HEADERS = "show-extended-headers"; private const string ACTION_SHOW_FORMATTING = "show-formatting"; private const string ACTION_DISCARD = "discard"; @@ -181,7 +181,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { { ACTION_ADD_ORIGINAL_ATTACHMENTS, on_pending_attachments }, { ACTION_CLOSE, on_close }, { ACTION_DISCARD, on_discard }, - { ACTION_COMPOSE_AS_HTML, on_toggle_action, null, "true", on_compose_as_html_toggled }, + { ACTION_TEXT_FORMAT, null, "s", "'html'", on_text_format }, { ACTION_DETACH, on_detach }, { ACTION_OPEN_INSPECTOR, on_open_inspector }, { ACTION_SELECT_DICTIONARY, on_select_dictionary }, @@ -377,13 +377,17 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { [GtkChild] private Gtk.Box toolbar_box; [GtkChild] private Gtk.Box top_buttons; [GtkChild] private Gtk.Box bottom_buttons; + [GtkChild] private Gtk.MenuButton font_button; + [GtkChild] private Gtk.Stack font_button_stack; + [GtkChild] private Gtk.MenuButton font_size_button; + [GtkChild] private Gtk.Image font_color_icon; + [GtkChild] private Gtk.MenuButton text_format_button; + [GtkChild] private Gtk.Button insert_link_button; [GtkChild] private Gtk.Button select_dictionary_button; [GtkChild] - private Gtk.MenuButton menu_button; - [GtkChild] private Gtk.Label info_label; [GtkChild] @@ -392,9 +396,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private GLib.SimpleActionGroup composer_actions = new GLib.SimpleActionGroup(); private GLib.SimpleActionGroup editor_actions = new GLib.SimpleActionGroup(); - private Menu html_menu; - private Menu plain_menu; - private Menu context_menu_model; private Menu context_menu_rich_text; private Menu context_menu_plain_text; @@ -549,8 +550,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { Gtk.Builder builder = new Gtk.Builder.from_resource( "/org/gnome/Geary/composer-menus.ui" ); - this.html_menu = (Menu) builder.get_object("html_menu_model"); - this.plain_menu = (Menu) builder.get_object("plain_menu_model"); this.context_menu_model = (Menu) builder.get_object("context_menu_model"); this.context_menu_rich_text = (Menu) builder.get_object("context_menu_rich_text"); this.context_menu_plain_text = (Menu) builder.get_object("context_menu_plain_text"); @@ -614,6 +613,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { ((Gtk.CellRendererText) cells.data).ellipsize = END; load_entry_completions(); + + update_color_icon.begin(Util.Gtk.rgba(0, 0, 0, 0)); } public Widget.from_mailbox(Application.Client application, @@ -757,8 +758,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { // model and hence the menu_button constructs a new // popover. this.composer_actions.change_action_state( - ACTION_COMPOSE_AS_HTML, - this.application.config.compose_as_html + ACTION_TEXT_FORMAT, + this.application.config.compose_as_html ? "html" : "plain" ); set_mode(DETACHED); @@ -1149,7 +1150,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { ACTION_SHOW_EXTENDED_HEADERS, false ); entries_users.change_action_state( - ACTION_COMPOSE_AS_HTML, this.application.config.compose_as_html + ACTION_TEXT_FORMAT, + this.application.config.compose_as_html ? "html" : "plain" ); } @@ -2119,9 +2121,9 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { action.change_state(!action.state.get_boolean()); } - private void on_compose_as_html_toggled(SimpleAction? action, Variant? new_state) { - bool compose_as_html = new_state.get_boolean(); - action.set_state(compose_as_html); + private void on_text_format(SimpleAction? action, Variant? new_state) { + bool compose_as_html = new_state.get_string() == "html"; + action.set_state(new_state.get_string()); foreach (string html_action in HTML_ACTIONS) get_action(html_action).set_enabled(compose_as_html); @@ -2132,11 +2134,10 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { show_formatting.set_enabled(compose_as_html); update_formatting_toolbar(); - this.menu_button.menu_model = (compose_as_html) ? this.html_menu : this.plain_menu; - this.editor.set_rich_text(compose_as_html); this.application.config.compose_as_html = compose_as_html; + this.text_format_button.popover.popdown(); } private void on_show_extended_headers_toggled(GLib.SimpleAction? action, @@ -2165,10 +2166,14 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { } private void on_font_family(SimpleAction action, Variant? param) { + string font = param.get_string(); this.editor.execute_editing_command_with_argument( - "fontname", param.get_string() + "fontname", font ); - action.set_state(param.get_string()); + action.set_state(font); + + this.font_button_stack.visible_child_name = font; + this.font_button.popover.popdown(); } private void on_font_size(SimpleAction action, Variant? param) { @@ -2182,15 +2187,35 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { this.editor.execute_editing_command_with_argument("fontsize", size); action.set_state(param.get_string()); + + this.font_size_button.popover.popdown(); + } + + private async void update_color_icon(Gdk.RGBA color) { + var theme = Gtk.IconTheme.get_default(); + var icon = theme.lookup_icon("font-color-symbolic", 16, 0); + Gdk.RGBA fg_color = Util.Gtk.rgba(0, 0, 0, 1); + this.get_style_context().lookup_color("theme_fg_color", out fg_color); + + try { + var pixbuf = yield icon.load_symbolic_async(fg_color, color, null, null, null); + this.font_color_icon.pixbuf = pixbuf; + } catch(Error e) { + warning("Could not load icon `font-color-symbolic`!"); + this.font_color_icon.icon_name = "font-color-symbolic"; + } } private void on_select_color() { Gtk.ColorChooserDialog dialog = new Gtk.ColorChooserDialog(_("Select Color"), this.container.top_window); if (dialog.run() == Gtk.ResponseType.OK) { + var rgba = dialog.get_rgba(); this.editor.execute_editing_command_with_argument( - "forecolor", dialog.get_rgba().to_string() + "forecolor", rgba.to_string() ); + + this.update_color_icon.begin(rgba); } dialog.destroy(); } @@ -2675,6 +2700,8 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { ACTION_FONT_FAMILY, context.font_family ); + this.update_color_icon.begin(context.font_color); + if (context.font_size < 11) this.editor_actions.change_action_state(ACTION_FONT_SIZE, "small"); else if (context.font_size > 20) diff --git a/src/client/util/util-gtk.vala b/src/client/util/util-gtk.vala index 70864ac9..1d41f2e5 100644 --- a/src/client/util/util-gtk.vala +++ b/src/client/util/util-gtk.vala @@ -218,4 +218,13 @@ namespace Util.Gtk { return new_url; } + public Gdk.RGBA rgba(double red, double green, double blue, double alpha) { + return Gdk.RGBA() { + red = red, + green = green, + blue = blue, + alpha = alpha + }; + } + } diff --git a/test/client/composer/composer-web-view-test.vala b/test/client/composer/composer-web-view-test.vala index 64828f04..97c2af29 100644 --- a/test/client/composer/composer-web-view-test.vala +++ b/test/client/composer/composer-web-view-test.vala @@ -35,15 +35,20 @@ public class Composer.WebViewTest : Components.WebViewTestCase } public void edit_context() throws Error { - assert(!(new WebView.EditContext("0,,,").is_link)); - assert(new WebView.EditContext("1,,,").is_link); - assert(new WebView.EditContext("1,url,,").link_url == "url"); + assert(!(new WebView.EditContext("0;;;;").is_link)); + assert(new WebView.EditContext("1;;;;").is_link); + assert(new WebView.EditContext("1;url;;;").link_url == "url"); - assert(new WebView.EditContext("0,,Helvetica,").font_family == "sans"); - assert(new WebView.EditContext("0,,Times New Roman,").font_family == "serif"); - assert(new WebView.EditContext("0,,Courier,").font_family == "monospace"); + assert(new WebView.EditContext("0;;Helvetica;;").font_family == "sans"); + assert(new WebView.EditContext("0;;Times New Roman;;").font_family == "serif"); + assert(new WebView.EditContext("0;;Courier;;").font_family == "monospace"); - assert(new WebView.EditContext("0,,,12").font_size == 12); + assert(new WebView.EditContext("0;;;12;").font_size == 12); + + assert(new WebView.EditContext("0;;;;rgb(0, 0, 0)").font_color == Util.Gtk.rgba(0, 0, 0, 1)); + assert(new WebView.EditContext("0;;;;rgb(255, 0, 0)").font_color == Util.Gtk.rgba(1, 0, 0, 1)); + assert(new WebView.EditContext("0;;;;rgb(0, 255, 0)").font_color == Util.Gtk.rgba(0, 1, 0, 1)); + assert(new WebView.EditContext("0;;;;rgb(0, 0, 255)").font_color == Util.Gtk.rgba(0, 0, 1, 1)); } public void get_html() throws GLib.Error { diff --git a/test/js/composer-page-state-test.vala b/test/js/composer-page-state-test.vala index dfb8566c..6228cc68 100644 --- a/test/js/composer-page-state-test.vala +++ b/test/js/composer-page-state-test.vala @@ -127,7 +127,7 @@ class Composer.PageStateTest : Components.WebViewTestCase { Util.JS.to_string( run_javascript(@"new EditContext(document.getElementById('test')).encode()") .get_js_value() - ).has_prefix("1,url,")); + ).has_prefix("1;url;")); } catch (Util.JS.Error err) { print("Util.JS.Error: %s\n", err.message); assert_not_reached(); @@ -138,7 +138,7 @@ class Composer.PageStateTest : Components.WebViewTestCase { } public void edit_context_font() throws Error { - string html = "

para

"; + string html = "

para

"; load_body_fixture(html); try { @@ -146,7 +146,7 @@ class Composer.PageStateTest : Components.WebViewTestCase { Util.JS.to_string( run_javascript(@"new EditContext(document.getElementById('test')).encode()") .get_js_value() - ) == "0,,Comic Sans,144"); + ) == "0;;Comic Sans;144;rgb(255, 127, 1)"); } catch (Util.JS.Error err) { print("Util.JS.Error: %s\n", err.message); assert_not_reached(); diff --git a/ui/composer-menus.ui b/ui/composer-menus.ui index 2f4c342f..7acf20ab 100644 --- a/ui/composer-menus.ui +++ b/ui/composer-menus.ui @@ -1,64 +1,6 @@ - -
- - S_ans Serif - edt.font-family - sans - - - S_erif - edt.font-family - serif - - - _Fixed Width - edt.font-family - monospace - -
-
- - _Small - edt.font-size - small - - - _Medium - edt.font-size - medium - - - Lar_ge - edt.font-size - large - -
-
- - C_olor - edt.color - -
-
- - _Rich Text - win.compose-as-html - -
-
- - -
- - _Rich Text - win.compose-as-html - -
-
-
diff --git a/ui/composer-web-view.js b/ui/composer-web-view.js index e3870190..fc5dbf91 100644 --- a/ui/composer-web-view.js +++ b/ui/composer-web-view.js @@ -684,21 +684,24 @@ EditContext.prototype = { } this.fontFamily = fontFamily; this.fontSize = styles.getPropertyValue("font-size").replace("px", ""); + this.fontColor = styles.getPropertyValue("color"); }, equals: function(other) { return other != null && this.context == other.context && this.linkUrl == other.linkUrl && this.fontFamily == other.fontFamily - && this.fontSize == other.fontSize; + && this.fontSize == other.fontSize + && this.fontColor == other.fontColor; }, encode: function() { return [ this.context.toString(16), this.linkUrl, this.fontFamily, - this.fontSize - ].join(","); + this.fontSize, + this.fontColor + ].join(";"); } }; diff --git a/ui/composer-widget.ui b/ui/composer-widget.ui index 1288ac6e..94768a21 100644 --- a/ui/composer-widget.ui +++ b/ui/composer-widget.ui @@ -798,6 +798,137 @@
+ + + True + True + False + False + Remove text formatting + edt.remove-format + True + + + True + False + 16 + format-text-remove-symbolic + + + + +
+
+ + + True + False + 6 + start + + + True + True + font_menu + Change font type + + + True + False + horizontal + + + True + False + + + True + False + Sans Serif + start + + + sans + + + + + True + False + Serif + start + + + serif + + + + + True + False + Fixed Width + start + + + monospace + + + + + + + True + False + pan-down + + + + + + + + + True + True + edt.color + Change font color + + + True + False + + + + + + + True + True + font_size_menu + Change font size + + + True + False + horizontal + + + True + False + font-size-symbolic + + + + + True + False + pan-down + + + + + + True @@ -857,33 +988,6 @@ - - - True - False - 6 - start - - - True - True - False - False - Remove text formatting - edt.remove-format - True - - - True - False - 16 - format-text-remove-symbolic - - - - - -
@@ -963,15 +1067,16 @@
- + True False False False + rich_text_menu True - view-more-symbolic + format-text-bold-symbolic @@ -1064,4 +1169,59 @@ + + +
+ + S_ans Serif + edt.font-family + sans + + + S_erif + edt.font-family + serif + + + _Fixed Width + edt.font-family + monospace + +
+
+ + +
+ + _Small + edt.font-size + small + + + _Medium + edt.font-size + medium + + + Lar_ge + edt.font-size + large + +
+
+ + +
+ + _Rich Text + win.text-format + html + + + _Plain Text + win.text-format + plain + +
+
From d5a5afcc782cde5b065bcbd1e7a9f9e0d533ad73 Mon Sep 17 00:00:00 2001 From: James Westman Date: Wed, 15 Jan 2020 22:12:34 -0600 Subject: [PATCH 5/7] composer: More tweaks to match mockups - Move the attachment button(s) out of the headerbar into the action bar - Add a cancel button - Adjust some margins - Set popover positions to top - Remove subject from headerbar title to save horizontal space --- src/client/composer/composer-headerbar.vala | 9 --- src/client/composer/composer-widget.vala | 13 +-- src/client/composer/spell-check-popover.vala | 3 +- ui/composer-headerbar.ui | 84 ++----------------- ui/composer-link-popover.ui | 2 +- ui/composer-widget.ui | 85 ++++++++++++++++++++ 6 files changed, 104 insertions(+), 92 deletions(-) diff --git a/src/client/composer/composer-headerbar.vala b/src/client/composer/composer-headerbar.vala index 4fde97cd..0b1bbd53 100644 --- a/src/client/composer/composer-headerbar.vala +++ b/src/client/composer/composer-headerbar.vala @@ -26,10 +26,6 @@ public class Composer.Headerbar : Gtk.HeaderBar { [GtkChild] private Gtk.Label recipients_label; [GtkChild] - private Gtk.Button new_message_attach_button; - [GtkChild] - private Gtk.Box conversation_attach_buttons; - [GtkChild] private Gtk.Button save_and_close_button; @@ -56,11 +52,6 @@ public class Composer.Headerbar : Gtk.HeaderBar { recipients_button.tooltip_text = tooltip; } - public void set_show_pending_attachments(bool show) { - this.new_message_attach_button.visible = !show; - this.conversation_attach_buttons.visible = show; - } - internal void set_mode(Widget.PresentationMode mode) { switch (mode) { case Widget.PresentationMode.DETACHED: diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala index e09a8806..6c490cf5 100644 --- a/src/client/composer/composer-widget.vala +++ b/src/client/composer/composer-widget.vala @@ -373,6 +373,10 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { private Gtk.Widget recipients; [GtkChild] private Gtk.Box header_area; + + [GtkChild] private Gtk.Button new_message_attach_button; + [GtkChild] private Gtk.Box conversation_attach_buttons; + [GtkChild] private Gtk.Revealer formatting; [GtkChild] private Gtk.Box toolbar_box; [GtkChild] private Gtk.Box top_buttons; @@ -1431,10 +1435,6 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { if (this.container != null) { this.container.top_window.title = subject; } - - if (this.application.config.desktop_environment != UNITY) { - this.header.title = subject; - } } internal void set_mode(PresentationMode new_mode) { @@ -1802,7 +1802,10 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface { } } } - this.header.set_show_pending_attachments(manual_enabled); + + this.new_message_attach_button.visible = !manual_enabled; + this.conversation_attach_buttons.visible = manual_enabled; + return have_added; } diff --git a/src/client/composer/spell-check-popover.vala b/src/client/composer/spell-check-popover.vala index c04183c0..44dfa021 100644 --- a/src/client/composer/spell-check-popover.vala +++ b/src/client/composer/spell-check-popover.vala @@ -60,6 +60,8 @@ public class SpellCheckPopover { this.is_lang_visible = is_active || is_visible; Gtk.Box box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 6); + box.margin = 6; + box.margin_start = 12; lang_name = Util.International.language_name_from_locale(lang_code); country_name = Util.International.country_name_from_locale(lang_code); @@ -69,7 +71,6 @@ public class SpellCheckPopover { label_text += " (" + country_name + ")"; Gtk.Label label = new Gtk.Label(label_text); label.set_halign(Gtk.Align.START); - label.set_size_request(-1, 24); box.pack_start(label, false, false); diff --git a/ui/composer-headerbar.ui b/ui/composer-headerbar.ui index 930854f0..2e2501d0 100644 --- a/ui/composer-headerbar.ui +++ b/ui/composer-headerbar.ui @@ -5,6 +5,11 @@