Host new composers in ComposerBox, not in ComposerEmbed

This allows us to avoid messing with the state of the conversation
viewer when we're composing a new message.  Instead, we hide it
completely and show only the ComposerBox.

The styling of paned composer is changed to resemble that of the inline
composers. Because of restrictions on what styles are available for
various widgets, the ComposerBox becomes a Gtk.Frame, and its border
simulates the margin while some padding and an inset shadow simulate the
border. I haven't figured out how to do an outset box-shadow.

https://bugzilla.gnome.org/show_bug.cgi?id=743670
This commit is contained in:
Robert Schroll 2015-01-29 03:19:14 -05:00
parent ec41d86fd1
commit 787e92e731
7 changed files with 60 additions and 77 deletions

View file

@ -2139,7 +2139,8 @@ public class GearyController : Geary.BaseObject {
widget.destroy.connect(on_composer_widget_destroy);
if (inline) {
if (widget.state == ComposerWidget.ComposerState.PANED)
if (widget.state == ComposerWidget.ComposerState.NEW ||
widget.state == ComposerWidget.ComposerState.PANED)
main_window.conversation_viewer.set_paned_composer(widget);
else
new ComposerEmbed(widget, main_window.conversation_viewer, referred); // is_draft
@ -2181,7 +2182,7 @@ public class GearyController : Geary.BaseObject {
// it if it hasn't been modified; otherwise open a new composer in a new window.
if (compose_type == ComposerWidget.ComposeType.NEW_MESSAGE) {
foreach (ComposerWidget cw in composer_widgets) {
if (cw.state == ComposerWidget.ComposerState.INLINE_NEW) {
if (cw.state == ComposerWidget.ComposerState.NEW) {
if (!cw.blank) {
inline = false;
return true;

View file

@ -145,9 +145,14 @@ public class MainWindow : Gtk.ApplicationWindow {
border-top-width: 0px;
border-bottom-width: 0px;
}
ComposerBox {
border: 16px solid #ccc;
box-shadow: 0 0 0 1px rgba(0,0,0,0.4) inset;
padding: 1px;
}
GtkBox GtkHeaderBar {
border-radius: 0px;
}
}
""";
if(Gtk.MAJOR_VERSION >= 3 && Gtk.MINOR_VERSION >= 14) {

View file

@ -4,9 +4,10 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public class ComposerBox : Gtk.EventBox, ComposerContainer {
public class ComposerBox : Gtk.Frame, ComposerContainer {
private ComposerWidget composer;
private Gee.Set<Geary.App.Conversation>? prev_selection = null;
private bool has_accel_group = false;
public Gtk.Window top_window {
@ -20,6 +21,13 @@ public class ComposerBox : Gtk.EventBox, ComposerContainer {
composer.editor.focus_in_event.connect(on_focus_in);
composer.editor.focus_out_event.connect(on_focus_out);
show();
if (composer.state == ComposerWidget.ComposerState.NEW) {
ConversationListView conversation_list_view = ((MainWindow) GearyApplication.
instance.controller.main_window).conversation_list_view;
prev_selection = conversation_list_view.get_selected_conversations();
conversation_list_view.get_selection().unselect_all();
}
}
public Gtk.Widget? remove_composer() {
@ -63,6 +71,17 @@ public class ComposerBox : Gtk.EventBox, ComposerContainer {
composer.state = ComposerWidget.ComposerState.DETACHED;
composer.editor.focus_in_event.disconnect(on_focus_in);
composer.editor.focus_out_event.disconnect(on_focus_out);
if (prev_selection != null) {
ConversationListView conversation_list_view = ((MainWindow) GearyApplication.
instance.controller.main_window).conversation_list_view;
if (prev_selection.is_empty)
// Need to trigger "No messages selected"
conversation_list_view.conversations_selected(prev_selection);
else
conversation_list_view.select_conversations(prev_selection);
prev_selection = null;
}
}
public void close_container() {

View file

@ -10,7 +10,6 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
private ComposerWidget composer;
private ConversationViewer conversation_viewer;
private Gee.Set<Geary.App.Conversation>? prev_selection = null;
private string embed_id;
private bool setting_inner_scroll;
private bool scrolled_to_bottom = false;
@ -24,31 +23,23 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
}
public ComposerEmbed(ComposerWidget composer, ConversationViewer conversation_viewer,
Geary.Email? referred) {
Geary.Email referred) {
this.composer = composer;
this.conversation_viewer = conversation_viewer;
halign = Gtk.Align.FILL;
valign = Gtk.Align.FILL;
WebKit.DOM.HTMLElement? email_element = null;
if (referred != null && composer.state != ComposerWidget.ComposerState.INLINE_NEW) {
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);
}
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";
if (email_element == null) {
ConversationListView conversation_list_view = ((MainWindow) GearyApplication.
instance.controller.main_window).conversation_list_view;
prev_selection = conversation_list_view.get_selected_conversations();
conversation_list_view.get_selection().unselect_all();
warning("Embedded composer could not find email to follow.");
email_element = conversation_viewer.web_view.get_dom_document().get_element_by_id(
"placeholder") as WebKit.DOM.HTMLElement;
}
try {
conversation_viewer.show_conversation_div();
email_element.insert_adjacent_html("afterend",
@"<div id='$embed_id' class='composer_embed'></div>");
} catch (Error error) {
@ -69,31 +60,27 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
private void on_realize() {
update_style();
if (composer.state != ComposerWidget.ComposerState.INLINE_NEW) {
Gtk.ScrolledWindow win = (Gtk.ScrolledWindow) composer.editor.parent;
win.get_vscrollbar().hide();
composer.editor.vadjustment.value_changed.connect(on_inner_scroll);
composer.editor.vadjustment.changed.connect(on_adjust_changed);
composer.editor.user_changed_contents.connect(on_inner_size_changed);
reroute_scroll_handling(this);
}
Gtk.ScrolledWindow win = (Gtk.ScrolledWindow) composer.editor.parent;
win.get_vscrollbar().hide();
composer.editor.vadjustment.value_changed.connect(on_inner_scroll);
composer.editor.vadjustment.changed.connect(on_adjust_changed);
composer.editor.user_changed_contents.connect(on_inner_size_changed);
reroute_scroll_handling(this);
}
private void on_loaded() {
if (composer.state != ComposerWidget.ComposerState.INLINE_NEW) {
try {
composer.editor.get_dom_document().body.get_class_list().add("embedded");
} catch (Error error) {
debug("Error setting class of editor: %s", error.message);
}
Idle.add(() => {
recalc_height();
conversation_viewer.compose_overlay.queue_resize();
return false;
});
try {
composer.editor.get_dom_document().body.get_class_list().add("embedded");
} catch (Error error) {
debug("Error setting class of editor: %s", error.message);
}
Idle.add(() => {
recalc_height();
conversation_viewer.compose_overlay.queue_resize();
return false;
});
}
private void reroute_scroll_handling(Gtk.Widget widget) {
@ -161,8 +148,7 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
int y_top = (int) (embed.offset_top + embed.client_top) - (int) vscroll;
int available_height = int.min(y_top + div_height, view_height) - int.max(y_top, 0);
if (available_height < 0 || available_height == div_height ||
composer.state == ComposerWidget.ComposerState.INLINE_NEW) {
if (available_height < 0 || available_height == div_height) {
// It fits in the available space, or it doesn't fit at all
allocation.y = y_top;
// When offscreen, make it very small to ensure scrolling during any edit
@ -179,10 +165,6 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
allocation.x = (int) (embed.offset_left + embed.client_left) - (int) hscroll;
allocation.width = (int) embed.client_width;
// INLINE_NEW handles its own scrolling.
if (composer.state == ComposerWidget.ComposerState.INLINE_NEW)
return true;
// Work out adjustment of composer web view
setting_inner_scroll = true;
composer.editor.vadjustment.set_value(allocation.y - y_top);
@ -290,17 +272,6 @@ public class ComposerEmbed : Gtk.EventBox, ComposerContainer {
} catch (Error error) {
warning("Could not remove embed from WebView: %s", error.message);
}
if (prev_selection != null) {
ConversationListView conversation_list_view = ((MainWindow) GearyApplication.
instance.controller.main_window).conversation_list_view;
if (prev_selection.is_empty)
// Need to trigger "No messages selected"
conversation_list_view.conversations_selected(prev_selection);
else
conversation_list_view.select_conversations(prev_selection);
prev_selection = null;
}
}
public void close_container() {

View file

@ -22,7 +22,7 @@ public class ComposerWidget : Gtk.EventBox {
public enum ComposerState {
DETACHED,
PANED,
INLINE_NEW,
NEW,
INLINE,
INLINE_COMPACT
}
@ -263,7 +263,7 @@ public class ComposerWidget : Gtk.EventBox {
this.account = account;
this.compose_type = compose_type;
if (compose_type == ComposeType.NEW_MESSAGE)
state = ComposerState.INLINE_NEW;
state = ComposerState.NEW;
else if (compose_type == ComposeType.FORWARD)
state = ComposerState.INLINE;
else

View file

@ -231,6 +231,10 @@ public class ConversationViewer : Gtk.Box {
Configuration config = GearyApplication.instance.config;
config.bind(Configuration.COMPOSER_PANE_POSITION_KEY, composer_paned, "position");
pack_start(composer_paned);
composer_boxes.notify["visible"].connect(() => {
if (!composer_boxes.visible && !message_overlay.visible)
message_overlay.show();
});
conversation_find_bar = new ConversationFindBar(web_view);
conversation_find_bar.no_show_all = true;
@ -243,6 +247,8 @@ public class ConversationViewer : Gtk.Box {
ComposerBox container = new ComposerBox(composer);
composer_boxes.pack_start(container);
composer_boxes.show();
if (composer.state == ComposerWidget.ComposerState.NEW)
message_overlay.hide();
}
public Geary.Email? get_last_message() {
@ -2437,10 +2443,5 @@ public class ConversationViewer : Gtk.Box {
return current_folder != null && current_folder.special_folder_type
== Geary.SpecialFolderType.DRAFTS;
}
// The Composer may need to adjust the mode back to conversation
public void show_conversation_div() {
set_mode(DisplayMode.CONVERSATION);
}
}

View file

@ -106,20 +106,6 @@ hr {
}
.composer_embed {
position: absolute;
top: 0px; /* margin-top has impact here, despite absolute positioning (!?) */
bottom: 16px;
left: 16px;
right: 16px;
width: auto;
}
.email + .composer_embed {
position: relative;
top: auto;
bottom: auto;
left: auto;
right: auto;
width: 100%;
height: 300px;
}