[geary/wip/730682-refine-convo-list] Add an action bar to the conversation list.
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/730682-refine-convo-list] Add an action bar to the conversation list.
- Date: Wed, 3 Jan 2018 09:03:01 +0000 (UTC)
commit 3c113bfcf89794413a0731e6ea79017c8330f65b
Author: Michael James Gratton <mike vee net>
Date: Wed Dec 27 00:32:08 2017 +1030
Add an action bar to the conversation list.
Based on design at:
https://wiki.gnome.org/Apps/Geary/Design/ConversationLifecycle
* src/client/components/conversation-action-bar.vala: Action bar
implementation to manage which buttons are visible based on the
selected folder.
* src/client/components/main-window.vala (MainWindow): Add action bar to
the main window when constructed.
src/CMakeLists.txt | 1 +
src/client/components/conversation-action-bar.vala | 191 +++++++++++++++
src/client/components/main-window.vala | 17 ++-
ui/CMakeLists.txt | 1 +
ui/conversation-action-bar.ui | 248 ++++++++++++++++++++
ui/main-window.ui | 22 ++-
6 files changed, 474 insertions(+), 6 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 78a1ec2..fb88f9b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -340,6 +340,7 @@ client/accounts/add-edit-page.vala
client/accounts/login-dialog.vala
client/components/client-web-view.vala
+client/components/conversation-action-bar.vala
client/components/count-badge.vala
client/components/empty-placeholder.vala
client/components/folder-popover.vala
diff --git a/src/client/components/conversation-action-bar.vala
b/src/client/components/conversation-action-bar.vala
new file mode 100644
index 0000000..9ceaba6
--- /dev/null
+++ b/src/client/components/conversation-action-bar.vala
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2017 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+/**
+ * A context-sensitive set of action buttons for a conversation.
+ */
+[GtkTemplate (ui = "/org/gnome/Geary/conversation-action-bar.ui")]
+public class ConversationActionBar : Gtk.ActionBar {
+
+ private Geary.Account? account = null;
+
+ private bool has_archive = false;
+ private bool has_trash = false;
+
+ private FolderPopover copy_folder_menu = new FolderPopover();
+ private FolderPopover move_folder_menu = new FolderPopover();
+
+ [GtkChild]
+ private Gtk.Grid flag_actions;
+ [GtkChild]
+ private Gtk.Button mark_read_action;
+ [GtkChild]
+ private Gtk.Button mark_unread_action;
+ [GtkChild]
+ private Gtk.Button mark_starred_action;
+ [GtkChild]
+ private Gtk.Button mark_unstarred_action;
+
+ [GtkChild]
+ private Gtk.Grid folder_actions;
+ [GtkChild]
+ private Gtk.Button archive_action;
+ [GtkChild]
+ private Gtk.Button restore_action;
+ [GtkChild]
+ private Gtk.MenuButton copy_action;
+ [GtkChild]
+ private Gtk.MenuButton move_action;
+
+ [GtkChild]
+ private Gtk.Grid destructive_actions;
+ [GtkChild]
+ private Gtk.Button junk_action;
+ [GtkChild]
+ private Gtk.Button trash_action;
+ [GtkChild]
+ private Gtk.Button delete_action;
+
+
+ public ConversationActionBar() {
+ this.copy_action.popover = copy_folder_menu;
+ this.move_action.popover = move_folder_menu;
+ }
+
+ public void set_account(Geary.Account account) {
+ if (this.account != null) {
+ this.account.folders_special_type.disconnect(on_special_folder_changed);
+ }
+
+ this.account = account;
+ this.account.folders_special_type.connect(on_special_folder_changed);
+ update_account();
+ }
+
+ public void update_location(Geary.Folder location) {
+ Gtk.Button? primary_action = null;
+ bool show_flag_actions = false;
+ bool show_folder_actions = false;
+ bool show_junk = false;
+ bool show_trash = false;
+ bool show_delete = false;
+
+ switch (location.special_folder_type) {
+ case Geary.SpecialFolderType.INBOX:
+ if (this.has_archive) {
+ primary_action = archive_action;
+ }
+ show_flag_actions = true;
+ show_folder_actions = true;
+ show_junk = true;
+ show_trash = true;
+ break;
+
+ case Geary.SpecialFolderType.ARCHIVE:
+ case Geary.SpecialFolderType.NONE:
+ primary_action = restore_action;
+ show_flag_actions = true;
+ show_folder_actions = true;
+ show_junk = true;
+ show_trash = true;
+ break;
+
+ case Geary.SpecialFolderType.SENT:
+ show_flag_actions = true;
+ show_trash = true;
+ break;
+
+ case Geary.SpecialFolderType.DRAFTS:
+ show_trash = true;
+ break;
+
+ case Geary.SpecialFolderType.TRASH:
+ primary_action = restore_action;
+ show_junk = true;
+ show_delete = true;
+ break;
+
+ case Geary.SpecialFolderType.SPAM:
+ primary_action = restore_action;
+ show_delete = true;
+ break;
+
+ case Geary.SpecialFolderType.OUTBOX:
+ show_delete = true;
+ break;
+
+ default:
+ // XXX remainder (Search, All, Flagged, etc) are all
+ // conversation specific, so examine it/them and work out
+ // what to do here
+ show_flag_actions = true;
+ break;
+ }
+
+ // XXX just always hide these for now while the UX is sorted out
+ //this.flag_actions.set_visible(show_flag_actions);
+ this.flag_actions.set_visible(false);
+ update_action_pair(this.mark_read_action, this.mark_unread_action);
+ update_action_pair(this.mark_starred_action, this.mark_unstarred_action);
+
+ this.folder_actions.set_visible(primary_action != null || show_folder_actions);
+ this.archive_action.set_visible(primary_action == this.archive_action);
+ this.restore_action.set_visible(primary_action == this.restore_action);
+ this.copy_action.set_visible(show_folder_actions);
+ this.move_action.set_visible(show_folder_actions);
+
+ if (show_trash && !this.has_trash) {
+ show_trash = false;
+ show_delete = true;
+ }
+ this.destructive_actions.set_visible(
+ show_junk || show_trash || show_delete
+ );
+ this.junk_action.set_visible(show_junk);
+ this.trash_action.set_visible(show_trash);
+ this.delete_action.set_visible(show_delete && !show_trash);
+ }
+
+ private void update_account() {
+ try {
+ this.has_archive = (
+ this.account.get_special_folder(Geary.SpecialFolderType.ARCHIVE) != null
+ );
+ } catch (Error err) {
+ debug("Could not get Archive for account: %s", this.account.to_string());
+ }
+ try {
+ this.has_trash = (
+ this.account.get_special_folder(Geary.SpecialFolderType.TRASH) != null
+ );
+ } catch (Error err) {
+ debug("Could not get Trash for account: %s", this.account.to_string());
+ }
+ }
+
+ private inline void update_action_pair(Gtk.Button primary, Gtk.Button secondary) {
+ bool show_primary = true;
+ string? secondary_action_name = secondary.get_action_name();
+ MainWindow? window = get_toplevel() as MainWindow;
+ if (window != null && secondary_action_name != null) {
+ Action? secondary_action = window.lookup_action(
+ secondary_action_name.substring(4) // chop off the "win."
+ );
+ if (secondary_action != null) {
+ show_primary = !secondary_action.get_enabled();
+ }
+ }
+
+ primary.set_visible(show_primary);
+ secondary.set_visible(!show_primary);
+ }
+
+ private void on_special_folder_changed() {
+ update_account();
+ }
+
+}
diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala
index 55ede0c..8d8c318 100644
--- a/src/client/components/main-window.vala
+++ b/src/client/components/main-window.vala
@@ -42,7 +42,10 @@ public class MainWindow : Gtk.ApplicationWindow {
public Geary.Folder? current_folder { get; private set; default = null; }
- private Geary.AggregateProgressMonitor progress_monitor = new Geary.AggregateProgressMonitor();
+ private ConversationActionBar conversation_list_actions =
+ new ConversationActionBar();
+ private Geary.AggregateProgressMonitor progress_monitor =
+ new Geary.AggregateProgressMonitor();
private Geary.ProgressMonitor? folder_progress = null;
private MonitoredSpinner spinner = new MonitoredSpinner();
@@ -59,9 +62,12 @@ public class MainWindow : Gtk.ApplicationWindow {
private Gtk.Box folder_box;
[GtkChild]
private Gtk.ScrolledWindow folder_list_scrolled;
+
[GtkChild]
private Gtk.Box conversation_box;
[GtkChild]
+ private Gtk.Grid conversation_list_grid;
+ [GtkChild]
private Gtk.ScrolledWindow conversation_list_scrolled;
// This is a frame so users can use F6/Shift-F6 to get to it
@@ -85,8 +91,10 @@ public class MainWindow : Gtk.ApplicationWindow {
this.conversation_list.marked_conversations_evaporated.connect(on_selection_mode_disabled);
this.conversation_list.selection_mode_enabled.connect(on_selection_mode_enabled);
this.conversation_list.visible_conversations_changed.connect(on_visible_conversations_changed);
-
this.conversation_list.load_more.connect(on_load_more);
+
+ this.conversation_list_grid.add(this.conversation_list_actions);
+
load_config(application.config);
restore_saved_window_state();
@@ -442,8 +450,11 @@ public class MainWindow : Gtk.ApplicationWindow {
this.current_folder.properties.notify.disconnect(update_headerbar);
// connect to new folder
- if (folder != null)
+ if (folder != null) {
folder.properties.notify.connect(update_headerbar);
+ this.conversation_list_actions.set_account(folder.account);
+ this.conversation_list_actions.update_location(this.current_folder);
+ }
// swap it in
this.current_folder = folder;
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index e8aaa76..5164978 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -12,6 +12,7 @@ set(RESOURCE_LIST
STRIPBLANKS "composer-widget.ui"
"composer-web-view.css"
"composer-web-view.js"
+ STRIPBLANKS "conversation-action-bar.ui"
STRIPBLANKS "conversation-email.ui"
STRIPBLANKS "conversation-email-attachment-view.ui"
STRIPBLANKS "conversation-email-menus.ui"
diff --git a/ui/conversation-action-bar.ui b/ui/conversation-action-bar.ui
new file mode 100644
index 0000000..c84cdc6
--- /dev/null
+++ b/ui/conversation-action-bar.ui
@@ -0,0 +1,248 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.2 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="ConversationActionBar" parent="GtkActionBar">
+ <property name="can_focus">False</property>
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkGrid" id="flag_actions">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="mark_read_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-mark-read</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">mail-unread-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="mark_unread_action">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-mark-unread</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">mail-read-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="mark_starred_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-mark-starred</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">starred-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="mark_unstarred_action">
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-mark-unstarred</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">starred-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="folder_actions">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="archive_action">
+ <property name="label" translatable="yes">Archive</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-archive</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="restore_action">
+ <property name="label" translatable="yes">Restore</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-restore</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="copy_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">tag-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkMenuButton" id="move_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">folder-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">start</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="destructive_actions">
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkButton" id="junk_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-junk</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">dialog-warning-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="trash_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="action_name">win.conversation-trash</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">user-trash-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_action">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="hexpand">True</property>
+ <property name="action_name">win.conversation-delete</property>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">edit-delete-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <style>
+ <class name="background"/>
+ <class name="geary-conversation-action-bar"/>
+ </style>
+ </template>
+</interface>
diff --git a/ui/main-window.ui b/ui/main-window.ui
index a7ecc85..d903fd6 100644
--- a/ui/main-window.ui
+++ b/ui/main-window.ui
@@ -75,11 +75,27 @@
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
- <object class="GtkScrolledWindow" id="conversation_list_scrolled">
- <property name="width_request">250</property>
+ <object class="GtkGrid" id="conversation_list_grid">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="hscrollbar_policy">never</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="conversation_list_scrolled">
+ <property name="width_request">250</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="hscrollbar_policy">never</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
</object>
</child>
<style>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]