Commit 7e446b15 authored by Niels De Graef's avatar Niels De Graef Committed by Michael Gratton

Use Popover for Move/Label menu. Bug 767431.

Signed-off-by: Niels De Graef's avatarNiels De Graef <nielsdegraef@gmail.com>
parent 4c20d90b
......@@ -332,7 +332,7 @@ client/accounts/login-dialog.vala
client/components/conversation-find-bar.vala
client/components/count-badge.vala
client/components/folder-menu.vala
client/components/folder-popover.vala
client/components/icon-factory.vala
client/components/main-toolbar.vala
client/components/main-window.vala
......
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
public class FolderMenu : Gtk.Menu {
private Gee.List<Geary.Folder> folder_list = new Gee.ArrayList<Geary.Folder>();
public signal void folder_selected(Geary.Folder folder);
public FolderMenu() {
}
public bool has_folder(Geary.Folder folder) {
return folder_list.contains(folder);
}
public void add_folder(Geary.Folder folder) {
// don't allow multiples and don't allow folders that can't be opened (that means they
// support almost no operations and have no content)
if (folder_list.contains(folder) || folder.properties.is_openable.is_impossible())
return;
// also don't allow local-only or virtual folders, which also have a limited set of
// operations
if (folder.properties.is_local_only || folder.properties.is_virtual)
return;
folder_list.add(folder);
folder_list.sort(folder_sort);
int index = folder_list.index_of(folder);
insert(build_menu_item(folder), index);
show_all();
}
public void enable_disable_folder(Geary.Folder folder, bool sensitive) {
int index = folder_list.index_of(folder);
if (index >= 0)
get_children().nth_data(index).sensitive = sensitive;
}
public void remove_folder(Geary.Folder folder) {
int index = folder_list.index_of(folder);
folder_list.remove(folder);
if (index >= 0)
remove(get_children().nth_data(index));
show_all();
}
public void clear() {
folder_list.clear();
this.foreach((w) => remove(w));
show_all();
}
private Gtk.MenuItem build_menu_item(Geary.Folder folder) {
Gtk.MenuItem menu_item = new Gtk.MenuItem.with_label(folder.path.to_string());
menu_item.activate.connect(() => {
folder_selected(folder);
});
return menu_item;
}
private static int folder_sort(Geary.Folder a, Geary.Folder b) {
return a.path.compare_to(b.path);
}
}
/* Copyright 2016 Software Freedom Conservancy Inc.
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
[GtkTemplate (ui = "/org/gnome/Geary/folder-popover.ui")]
public class FolderPopover : Gtk.Popover {
[GtkChild]
private Gtk.SearchEntry search_entry;
[GtkChild]
private Gtk.ListBox list_box;
private int filtered_folder_count = 0;
public signal void folder_selected(Geary.Folder folder);
public FolderPopover() {
list_box.set_filter_func(row_filter);
list_box.set_sort_func(row_sort);
this.show.connect(() => search_entry.grab_focus());
this.hide.connect(() => {
search_entry.set_text("");
invalidate_filter();
});
}
public bool has_folder(Geary.Folder folder) {
return get_row_with_folder(folder) != null;
}
public void add_folder(Geary.Folder folder) {
// don't allow multiples and don't allow folders that can't be opened (that means they
// support almost no operations and have no content)
if (has_folder(folder) || folder.properties.is_openable.is_impossible())
return;
// also don't allow local-only or virtual folders, which also have a limited set of
// operations
if (folder.properties.is_local_only || folder.properties.is_virtual)
return;
list_box.add(build_row(folder));
list_box.invalidate_sort();
}
public void enable_disable_folder(Geary.Folder folder, bool sensitive) {
Gtk.ListBoxRow row = get_row_with_folder(folder);
if (row != null)
row.sensitive = sensitive;
}
public void remove_folder(Geary.Folder folder) {
Gtk.ListBoxRow row = get_row_with_folder(folder);
if (row != null)
list_box.remove(row);
}
public Gtk.ListBoxRow? get_row_with_folder(Geary.Folder folder) {
Gtk.ListBoxRow result = null;
list_box.foreach((row) => {
if (row.get_data<Geary.Folder>("folder") == folder)
result = row as Gtk.ListBoxRow;
});
return result;
}
public void clear() {
list_box.foreach((row) => list_box.remove(row));
}
private Gtk.ListBoxRow build_row(Geary.Folder folder) {
Gtk.ListBoxRow row = new Gtk.ListBoxRow();
row.get_style_context().add_class("geary-folder-popover-list-row");
row.set_data("folder", folder);
Gtk.Label label = new Gtk.Label(folder.path.to_string());
label.set_halign(Gtk.Align.START);
row.add(label);
return row;
}
[GtkCallback]
private void on_row_activated(Gtk.ListBoxRow? row) {
if (row != null) {
Geary.Folder folder = row.get_data<Geary.Folder>("folder");
folder_selected(folder);
}
this.hide();
}
[GtkCallback]
private void on_search_entry_activate() {
if (filtered_folder_count == 1) {
// Don't use get_row_at_index(0), or you will get the first row of the unfiltered list.
Gtk.ListBoxRow? row = list_box.get_row_at_y(0);
if (row != null)
on_row_activated(row);
} else if (filtered_folder_count > 0) {
list_box.get_row_at_y(0).grab_focus();
}
}
[GtkCallback]
private void on_search_entry_search_changed() {
invalidate_filter();
}
private void invalidate_filter() {
filtered_folder_count = 0;
list_box.invalidate_filter();
}
private bool row_filter(Gtk.ListBoxRow row) {
Gtk.Label label = row.get_child() as Gtk.Label;
if (label.label.down().contains(search_entry.text.down())) {
filtered_folder_count++;
return true;
}
return false;
}
private int row_sort(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2) {
Geary.Folder folder1 = row1.get_data<Geary.Folder>("folder");
Geary.Folder folder2 = row2.get_data<Geary.Folder>("folder");
return folder1.path.compare_to(folder2.path);
}
}
......@@ -6,8 +6,8 @@
// Draws the main toolbar.
public class MainToolbar : Gtk.Box {
public FolderMenu copy_folder_menu { get; private set; default = new FolderMenu(); }
public FolderMenu move_folder_menu { get; private set; default = new FolderMenu(); }
public FolderPopover copy_folder_menu { get; private set; default = new FolderPopover(); }
public FolderPopover move_folder_menu { get; private set; default = new FolderPopover(); }
public string account { get; set; }
public string folder { get; set; }
public bool show_close_button { get; set; default = false; }
......@@ -103,9 +103,9 @@ public class MainToolbar : Gtk.Box {
insert.clear();
insert.add(conversation_header.create_menu_button("marker-symbolic", mark_menu,
GearyController.ACTION_MARK_AS_MENU));
insert.add(conversation_header.create_menu_button(rtl ? "tag-rtl-symbolic" : "tag-symbolic",
insert.add(conversation_header.create_popover_button(rtl ? "tag-rtl-symbolic" : "tag-symbolic",
copy_folder_menu, GearyController.ACTION_COPY_MENU));
insert.add(conversation_header.create_menu_button("folder-symbolic", move_folder_menu,
insert.add(conversation_header.create_popover_button("folder-symbolic", move_folder_menu,
GearyController.ACTION_MOVE_MENU));
conversation_header.add_start(conversation_header.create_pill_buttons(insert));
......
......@@ -102,6 +102,27 @@ public interface PillBar : Gtk.Container {
return b;
}
/**
* Given an icon, popover, and action, creates a button that triggers the popover and the action.
*/
public virtual Gtk.MenuButton create_popover_button(string? icon_name, Gtk.Popover? popover, string action_name) {
Gtk.MenuButton b = new Gtk.MenuButton();
setup_button(b, icon_name, action_name);
b.set_popover(popover);
b.clicked.connect(() => popover.show_all());
if (b.related_action != null) {
b.related_action.activate.connect(() => {
b.clicked();
});
// Null out the action since by connecting it to clicked
// above, invoking would cause an infinite loop otherwise.
b.related_action = null;
}
return b;
}
/**
* Given a list of buttons, creates a "pill-style" tool item that can be appended to this
* toolbar. Optionally adds spacers "before" and "after" the buttons (those terms depending
......
......@@ -10,6 +10,7 @@ set(RESOURCE_LIST
STRIPBLANKS "composer_accelerators.ui"
STRIPBLANKS "edit_alternate_emails.glade"
STRIPBLANKS "find_bar.glade"
STRIPBLANKS "folder-popover.ui"
STRIPBLANKS "login.glade"
STRIPBLANKS "password-dialog.glade"
STRIPBLANKS "preferences.glade"
......
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.14"/>
<template class="FolderPopover" parent="GtkPopover">
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="container">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_start">6</property>
<property name="margin_end">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkSearchEntry" id="search_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="primary_icon_name">edit-find-symbolic</property>
<property name="primary_icon_activatable">False</property>
<property name="primary_icon_sensitive">False</property>
<signal name="activate" handler="on_search_entry_activate" swapped="no"/>
<signal name="search_changed" handler="on_search_entry_search_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolled">
<property name="min_content_width">200</property>
<property name="min_content_height">320</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkListBox" id="list_box">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="activate_on_single_click">True</property>
<signal name="row_activated" handler="on_row_activated" swapped="no"/>
<style>
<class name="geary-folder-popover-list"/>
</style>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</template>
</interface>
......@@ -45,3 +45,13 @@ GtkBox.vertical GtkHeaderBar {
.geary-titlebar-left:dir(rtl) {
border-top-left-radius: 0px;
}
row.geary-folder-popover-list-row {
padding: 6px;
border-color: @borders;
border-style: groove;
border-bottom-width: 1px;
}
row.geary-folder-popover-list-row > label {
color: @theme_text_color;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment