[geary/wip/713739-inline: 23/37] Allow multiple inline composers
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/713739-inline: 23/37] Allow multiple inline composers
- Date: Tue, 20 May 2014 20:17:35 +0000 (UTC)
commit b715fb78aea75a7847e9c1d5638e7f7e51241075
Author: Robert Schroll <rschroll gmail com>
Date: Thu May 15 16:33:25 2014 -0700
Allow multiple inline composers
Replying to a message that already has a composer open will result in
that composer refocusing. It should also adjust the Reply/Reply
All/Forward status, but that's still to come.
When switching away from a conversation with inline composers, the only
option is to cancel the operation or discard all compositions. This
will be fixed by adding automatic saving of drafts in this case.
src/CMakeLists.txt | 1 +
src/client/application/geary-controller.vala | 61 ++++++++++++--------
src/client/composer/composer-embed.vala | 14 +++--
src/client/composer/composer-widget.vala | 33 ++++++++---
.../conversation-list/conversation-list-view.vala | 4 +-
.../conversation-viewer/conversation-web-view.vala | 9 ---
src/client/util/util-random.vala | 15 +++++
theming/message-viewer.css | 14 +---
8 files changed, 90 insertions(+), 61 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5f339a8..40ce72a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -375,6 +375,7 @@ client/util/util-files.vala
client/util/util-gravatar.vala
client/util/util-gtk.vala
client/util/util-international.vala
+client/util/util-random.vala
client/util/util-webkit.vala
)
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 2465129..1d5d132 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -111,8 +111,6 @@ public class GearyController : Geary.BaseObject {
// List of windows we're waiting to close before Geary closes.
private Gee.List<ComposerWidget> waiting_to_close = new Gee.ArrayList<ComposerWidget>();
- public ComposerEmbed? inline_composer = null;
-
/**
* Fired when the currently selected account has changed.
*/
@@ -1817,6 +1815,9 @@ public class GearyController : Geary.BaseObject {
if (current_account == null)
return;
+ if (!should_create_new_composer(compose_type, referred))
+ return;
+
ComposerWidget widget;
if (mailto != null) {
widget = new ComposerWidget.from_mailto(current_account, mailto);
@@ -1840,42 +1841,52 @@ public class GearyController : Geary.BaseObject {
composer_widgets.add(widget);
widget.destroy.connect(on_composer_widget_destroy);
- if (abandon_existing_composition(widget))
- inline_composer = new ComposerEmbed(widget, main_window.conversation_viewer, referred);
+ new ComposerEmbed(widget, main_window.conversation_viewer, referred);
}
- public bool abandon_existing_composition(ComposerWidget? new_composer = null) {
- if (inline_composer == null)
+ public bool should_create_new_composer(
+ ComposerWidget.ComposeType compose_type = ComposerWidget.ComposeType.NEW_MESSAGE,
+ Geary.Email? referred = null) {
+ if (!any_inline_composers())
+ return true;
+
+ if (compose_type != ComposerWidget.ComposeType.NEW_MESSAGE) {
+ foreach (ComposerWidget cw in composer_widgets) {
+ if (referred != null && referred.id.equal_to(cw.referred_id)) {
+ cw.change_compose_type(compose_type);
+ return false;
+ }
+ }
return true;
+ }
+ // TODO: Remove this in favor of automatically saving drafts
main_window.present();
AlertDialog dialog;
- // TODO: Clean up when closing (like delete_and_exit) / offer save option.
- if (new_composer != null)
- dialog = new AlertDialog(main_window, Gtk.MessageType.QUESTION,
- _("Do you want to discard the existing composition?"), null, Gtk.Stock.DISCARD,
- Gtk.Stock.CANCEL, _("Open New Composition Window"), Gtk.ResponseType.YES);
- else
- dialog = new AlertDialog(main_window, Gtk.MessageType.QUESTION,
- _("Do you want to discard the existing composition?"), null, Gtk.Stock.DISCARD,
- Gtk.Stock.CANCEL, _("Move Composition to New Window"), Gtk.ResponseType.YES);
+ dialog = new AlertDialog(main_window, Gtk.MessageType.QUESTION,
+ _("Closing inline composers."), null, Gtk.Stock.DISCARD, Gtk.Stock.CANCEL,
+ null, Gtk.ResponseType.NONE);
Gtk.ResponseType response = dialog.run();
if (response == Gtk.ResponseType.OK) {
- inline_composer.close();
- return true;
- }
- if (new_composer != null) {
- if (response == Gtk.ResponseType.YES)
- new ComposerWindow(new_composer);
- else
- new_composer.destroy();
- } else if (response == Gtk.ResponseType.YES) {
- inline_composer.on_detach();
+ Gee.List<ComposerWidget> composers_to_destroy = new Gee.ArrayList<ComposerWidget>();
+ foreach (ComposerWidget cw in composer_widgets) {
+ if (cw.inline)
+ composers_to_destroy.add(cw);
+ }
+ foreach(ComposerWidget cw in composers_to_destroy)
+ ((ComposerContainer) cw.parent).close();
return true;
}
return false;
}
+ public bool any_inline_composers() {
+ foreach (ComposerWidget cw in composer_widgets)
+ if (cw.inline)
+ return true;
+ return false;
+ }
+
private void on_composer_widget_destroy(Gtk.Widget sender) {
composer_widgets.remove((ComposerWidget) sender);
diff --git a/src/client/composer/composer-embed.vala b/src/client/composer/composer-embed.vala
index f59a8d5..b025d10 100644
--- a/src/client/composer/composer-embed.vala
+++ b/src/client/composer/composer-embed.vala
@@ -6,11 +6,10 @@
public class ComposerEmbed : Gtk.Box, ComposerContainer {
- private static string embed_id = "composer_embed";
-
private ComposerWidget composer;
private ConversationViewer conversation_viewer;
private Gee.Set<Geary.App.Conversation>? prev_selection = null;
+ private string embed_id;
public Gtk.Window top_window {
get { return (Gtk.Window) get_toplevel(); }
@@ -41,9 +40,13 @@ public class ComposerEmbed : Gtk.Box, ComposerContainer {
detach.clicked.connect(on_detach);
WebKit.DOM.HTMLElement? email_element = null;
- if (referred != null)
+ if (referred != null) {
email_element = conversation_viewer.web_view.get_dom_document().get_element_by_id(
conversation_viewer.get_div_id(referred.id)) as WebKit.DOM.HTMLElement;
+ embed_id = referred.id.to_string() + "_reply";
+ } else {
+ embed_id = random_string(10);
+ }
if (email_element == null) {
ConversationListView conversation_list_view = ((MainWindow) GearyApplication.
instance.controller.main_window).conversation_list_view;
@@ -56,7 +59,7 @@ public class ComposerEmbed : Gtk.Box, ComposerContainer {
try {
conversation_viewer.show_conversation_div();
email_element.insert_adjacent_html("afterend",
- @"<div id='$embed_id'></div>");
+ @"<div id='$embed_id' class='composer_embed'></div>");
} catch (Error error) {
debug("Error creating embed element: %s", error.message);
return;
@@ -121,7 +124,6 @@ public class ComposerEmbed : Gtk.Box, ComposerContainer {
}
public void vanish() {
- GearyApplication.instance.controller.inline_composer = null;
hide();
composer.editor.focus_in_event.disconnect(on_focus_in);
composer.editor.focus_out_event.disconnect(on_focus_out);
@@ -146,7 +148,7 @@ public class ComposerEmbed : Gtk.Box, ComposerContainer {
}
public void close() {
- if (GearyApplication.instance.controller.inline_composer == this)
+ if (visible)
vanish();
conversation_viewer.compose_overlay.remove(this);
}
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 70ec6b2..277436f 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -134,8 +134,14 @@ public class ComposerWidget : Gtk.EventBox {
set { ((Gtk.ToggleAction) actions.get_action(ACTION_COMPOSE_AS_HTML)).active = value; }
}
+ public bool inline {
+ get { return parent is ComposerEmbed && visible; }
+ }
+
public ComposeType compose_type { get; private set; default = ComposeType.NEW_MESSAGE; }
+ public Geary.EmailIdentifier? referred_id { get; private set; default = null; }
+
private ContactListStore? contact_list_store = null;
private string? body_html = null;
@@ -329,6 +335,7 @@ public class ComposerWidget : Gtk.EventBox {
from_multiple.changed.connect(on_from_changed);
if (referred != null) {
+ this.referred_id = referred.id;
switch (compose_type) {
case ComposeType.NEW_MESSAGE:
if (referred.to != null)
@@ -512,6 +519,16 @@ public class ComposerWidget : Gtk.EventBox {
}
}
+ private void set_focus() {
+ if (Geary.String.is_empty(to)) {
+ to_entry.grab_focus();
+ } else if (Geary.String.is_empty(subject)) {
+ subject_entry.grab_focus();
+ } else {
+ editor.grab_focus();
+ }
+ }
+
private void on_load_finished(WebKit.WebFrame frame) {
WebKit.DOM.HTMLElement? body = editor.get_dom_document().get_element_by_id(
BODY_ID) as WebKit.DOM.HTMLElement;
@@ -527,15 +544,7 @@ public class ComposerWidget : Gtk.EventBox {
protect_blockquote_styles();
- // Set focus.
- if (Geary.String.is_empty(to)) {
- to_entry.grab_focus();
- } else if (Geary.String.is_empty(subject)) {
- subject_entry.grab_focus();
- } else {
- editor.grab_focus();
- body.focus();
- }
+ set_focus();
// Ensure the editor is in correct mode re HTML
on_compose_as_html();
@@ -671,6 +680,12 @@ public class ComposerWidget : Gtk.EventBox {
update_from_field();
}
+ // TODO: Make this actually do something
+ public void change_compose_type(ComposeType new_type) {
+ container.present();
+ set_focus();
+ }
+
private bool can_save() {
return (drafts_folder != null && drafts_folder.get_open_state() == Geary.Folder.OpenState.BOTH
&& !drafts_folder.properties.create_never_returns_id && editor.can_undo());
diff --git a/src/client/conversation-list/conversation-list-view.vala
b/src/client/conversation-list/conversation-list-view.vala
index 8382637..4aac993 100644
--- a/src/client/conversation-list/conversation-list-view.vala
+++ b/src/client/conversation-list/conversation-list-view.vala
@@ -204,7 +204,7 @@ public class ConversationListView : Gtk.TreeView {
}
if (!get_selection().path_is_selected(path) &&
- !GearyApplication.instance.controller.abandon_existing_composition())
+ !GearyApplication.instance.controller.should_create_new_composer())
return true;
if (event.button == 3 && event.type == Gdk.EventType.BUTTON_PRESS) {
@@ -388,7 +388,7 @@ public class ConversationListView : Gtk.TreeView {
// Selects the first conversation, if nothing has been selected yet and we're not composing.
public void select_first_conversation() {
if (get_selected_path() == null &&
- GearyApplication.instance.controller.inline_composer == null) {
+ !GearyApplication.instance.controller.any_inline_composers()) {
set_cursor(new Gtk.TreePath.from_indices(0, -1), null, false);
}
}
diff --git a/src/client/conversation-viewer/conversation-web-view.vala
b/src/client/conversation-viewer/conversation-web-view.vala
index 56e85c0..3368d86 100644
--- a/src/client/conversation-viewer/conversation-web-view.vala
+++ b/src/client/conversation-viewer/conversation-web-view.vala
@@ -49,15 +49,6 @@ public class ConversationWebView : WebKit.WebView {
load_string(html_text, "text/html", "UTF8", "");
}
- private string random_string(int length) {
- // No upper case letters, since request gets lower-cased.
- string chars = "abcdefghijklmnopqrstuvwxyz";
- char[] random = new char[length];
- for (int i = 0; i < length; i++)
- random[i] = chars[Random.int_range(0, chars.length)];
- return (string) random;
- }
-
public override bool query_tooltip(int x, int y, bool keyboard_tooltip, Gtk.Tooltip tooltip) {
// Disable tooltips from within WebKit itself.
return false;
diff --git a/src/client/util/util-random.vala b/src/client/util/util-random.vala
new file mode 100644
index 0000000..e8939f4
--- /dev/null
+++ b/src/client/util/util-random.vala
@@ -0,0 +1,15 @@
+/* Copyright 2013-2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+private string random_string(int length) {
+ // No upper case letters, since request gets lower-cased.
+ string chars = "abcdefghijklmnopqrstuvwxyz";
+ char[] random = new char[length];
+ for (int i = 0; i < length; i++)
+ random[i] = chars[Random.int_range(0, chars.length)];
+ return (string) random;
+}
+
diff --git a/theming/message-viewer.css b/theming/message-viewer.css
index 9aa6b94..b7c10a9 100644
--- a/theming/message-viewer.css
+++ b/theming/message-viewer.css
@@ -88,7 +88,7 @@ hr {
box-shadow: inset 0px 0px 1px rgba(0,0,0,0.05);
}
-.email, #composer_embed {
+.email, .composer_embed {
border: 1px rgba(0,0,0,1) solid;
background-color: white;/* recv-normal */
color: black;
@@ -101,7 +101,7 @@ hr {
margin-top: 16px;
}
-#composer_embed {
+.composer_embed {
position: absolute;
top: 0px; /* margin-top has impact here, despite absolute positioning (!?) */
bottom: 16px;
@@ -109,7 +109,7 @@ hr {
right: 16px;
width: auto;
}
-.email + #composer_embed {
+.email + .composer_embed {
position: relative;
top: auto;
bottom: auto;
@@ -118,12 +118,6 @@ hr {
width: 100%;
height: 600px;
}
-/* For some reason, we can't absolutely position <embed>s or <object>s, so we wrap everything
- it in a div and then tell the <embed> to take up all the available space. */
-#composer_embed > embed {
- width: 100%;
- height: 100%;
-}
.email.sent {
background-color: white;/* sent-normal */
@@ -141,7 +135,7 @@ hr {
.email.starred .unstarred {
display: none;
}
-.email.read, #multiple_messages .email, #composer_embed {
+.email.read, #multiple_messages .email, .composer_embed {
border-color: rgba(0,0,0,0.4);
box-shadow: 0 3px 11px rgba(0,0,0,0.21);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]