summaryrefslogtreecommitdiffstats
path: root/src/tab-label.c
diff options
context:
space:
mode:
authorPatrick McDermott <pj@pehjota.net>2017-10-17 18:58:23 (EDT)
committer Patrick McDermott <pj@pehjota.net>2017-10-17 18:58:23 (EDT)
commit60574acaa26bc50cd83a9aa8f2c044cdca94c3dc (patch)
tree632bb33b61b1c7d17f1699b575144fd5f28afc5d /src/tab-label.c
parentcd8dbba39302c957d62a754e7b84b99d7e2aa91e (diff)
downloadmarquee-60574acaa26bc50cd83a9aa8f2c044cdca94c3dc.zip
marquee-60574acaa26bc50cd83a9aa8f2c044cdca94c3dc.tar.gz
marquee-60574acaa26bc50cd83a9aa8f2c044cdca94c3dc.tar.bz2
MqTabLabel: Add popover menu
Diffstat (limited to 'src/tab-label.c')
-rw-r--r--src/tab-label.c217
1 files changed, 216 insertions, 1 deletions
diff --git a/src/tab-label.c b/src/tab-label.c
index b00e026..549a10e 100644
--- a/src/tab-label.c
+++ b/src/tab-label.c
@@ -55,10 +55,225 @@ struct _MqTabLabelClass {
G_DEFINE_TYPE(MqTabLabel, mq_tab_label, GTK_TYPE_EVENT_BOX)
+static void
+reload_tab_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTabLabel *tab_label)
+{
+ webkit_web_view_reload(tab_label->web_view);
+ gtk_widget_hide(tab_label->popover);
+}
+
+static void
+new_tab_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTabLabel *tab_label)
+{
+ mq_tab_new(NULL, tab_label->tab_page);
+ gtk_widget_hide(tab_label->popover);
+}
+
+static void
+new_window_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTabLabel *tab_label)
+{
+ mq_application_add_window(mq_tab_get_application(tab_label->tab_page),
+ NULL);
+ gtk_widget_hide(tab_label->popover);
+}
+
+static void
+tab_list_button_toggled_cb(GtkToggleButton *toggle_button, GtkWidget *tab_list)
+{
+ if (gtk_toggle_button_get_active(toggle_button)) {
+ gtk_widget_show(tab_list);
+ } else {
+ gtk_widget_hide(tab_list);
+ }
+}
+
+static void
+create_tree_model_recurse(MqTab *node, GtkTreeStore *tree_store,
+ GtkTreeIter *parent_tree_iter)
+{
+ GtkTreeIter tree_iter;
+
+ for (; node; node = node->next) {
+ gtk_tree_store_append(tree_store, &tree_iter, parent_tree_iter);
+ gtk_tree_store_set(tree_store, &tree_iter, 0, node->title, -1);
+ create_tree_model_recurse(node->first_child, tree_store,
+ &tree_iter);
+ }
+}
+
+static GtkTreeModel *
+create_tree_model(MqTabLabel *tab_label)
+{
+ GtkTreeStore *tree_store;
+
+ tree_store = gtk_tree_store_new(1, G_TYPE_STRING);
+
+ create_tree_model_recurse(tab_label->tab_page->root->first_child,
+ tree_store, NULL);
+
+ return GTK_TREE_MODEL(tree_store);
+}
+
+static void
+row_activated_cb(GtkTreeView G_GNUC_UNUSED *tree_view, GtkTreePath *tree_path,
+ GtkTreeViewColumn G_GNUC_UNUSED *tree_view_column,
+ MqTabLabel *tab_label)
+{
+ gint *indices;
+ gint depth;
+
+ indices = gtk_tree_path_get_indices_with_depth(tree_path, &depth);
+ g_assert(depth == 1);
+ mq_window_set_current_tab(mq_tab_get_window(tab_label->tab_page),
+ indices[0] + 1);
+ gtk_widget_hide(tab_label->popover);
+}
+
+static GtkWidget *
+create_tab_list(MqTabLabel *tab_label)
+{
+ GtkWidget *tree_view;
+ GtkTreeSelection *tree_selection;
+ GtkCellRenderer *cell_renderer;
+ GtkWidget *scrolled_window;
+
+ tree_view = gtk_tree_view_new_with_model(create_tree_model(tab_label));
+ tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+ gtk_tree_selection_set_mode(tree_selection, GTK_SELECTION_BROWSE);
+ gtk_tree_selection_select_path(tree_selection,
+ gtk_tree_path_new_from_indices(
+ mq_window_get_current_tab(mq_tab_get_window(
+ tab_label->tab_page)) - 1, -1));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
+ gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view),
+ TRUE);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_view), TRUE);
+ gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(tree_view), TRUE);
+ g_signal_connect(tree_view, "row-activated",
+ G_CALLBACK(row_activated_cb), tab_label);
+
+ cell_renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
+ -1, NULL, cell_renderer, "text", 0, NULL);
+
+ scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_min_content_width(
+ GTK_SCROLLED_WINDOW(scrolled_window), 400);
+ gtk_scrolled_window_set_min_content_height(
+ GTK_SCROLLED_WINDOW(scrolled_window), 200);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
+
+ return scrolled_window;
+}
+
+#define BUTTON_ROWS 2
+#define BUTTON_COLS 4
+#define NEW_BUTTON(Y, X, ICON, TOOLTIP) \
+ do { \
+ buttons[Y * BUTTON_COLS + X] = gtk_button_new_from_icon_name(\
+ ICON, GTK_ICON_SIZE_BUTTON); \
+ gtk_widget_set_tooltip_text(buttons[Y * BUTTON_COLS + X], \
+ TOOLTIP); \
+ gtk_widget_set_can_focus(buttons[Y * BUTTON_COLS + X], FALSE); \
+ gtk_grid_attach(GTK_GRID(button_grid), \
+ buttons[Y * BUTTON_COLS + X], X, Y, 1, 1); \
+ } while (0)
+#define NEW_TOGGLE(Y, X, ICON, TOOLTIP) \
+ do { \
+ buttons[Y * BUTTON_COLS + X] = gtk_toggle_button_new(); \
+ gtk_button_set_image(GTK_BUTTON(buttons[Y * BUTTON_COLS + X]), \
+ gtk_image_new_from_icon_name(ICON, \
+ GTK_ICON_SIZE_BUTTON)); \
+ gtk_widget_set_tooltip_text(buttons[Y * BUTTON_COLS + X], \
+ TOOLTIP); \
+ gtk_widget_set_can_focus(buttons[Y * BUTTON_COLS + X], FALSE); \
+ gtk_grid_attach(GTK_GRID(button_grid), \
+ buttons[Y * BUTTON_COLS + X], X, Y, 1, 1); \
+ } while (0)
+#define CLICKED_CB(Y, X, CB) \
+ g_signal_connect(buttons[Y * BUTTON_COLS + X], "clicked", CB, tab_label)
+
+static void
+create_tab_popover(GtkWidget *widget, MqTabLabel *tab_label)
+{
+ GtkWidget *button_grid;
+ GtkWidget *buttons[BUTTON_ROWS * BUTTON_COLS];
+ GtkWidget *tab_list;
+ GtkWidget *tab_list_scrolled_window;
+ GtkWidget *box;
+
+ /* Set up button grid. */
+ button_grid = gtk_grid_new();
+ gtk_widget_set_halign(button_grid, GTK_ALIGN_CENTER);
+
+ /* Set up buttons. */
+ NEW_BUTTON(0, 0, "view-refresh", "Reload tab");
+ NEW_BUTTON(0, 1, "edit-copy", "Duplicate tab");
+ NEW_BUTTON(0, 2, "window-new", "Move tab to new window");
+ NEW_BUTTON(0, 3, "window-close", "Close tab");
+ NEW_BUTTON(1, 0, "tab-new-symbolic", "New tab");
+ NEW_BUTTON(1, 1, "window-new", "New window");
+ NEW_BUTTON(1, 2, "edit-undo", "Undo close tab");
+ NEW_TOGGLE(1, 3, "view-list-symbolic", "Tab list...");
+
+ CLICKED_CB(0, 0, G_CALLBACK(reload_tab_clicked_cb));
+ CLICKED_CB(1, 0, G_CALLBACK(new_tab_clicked_cb));
+ CLICKED_CB(1, 1, G_CALLBACK(new_window_clicked_cb));
+
+ /* Set up the tab list. */
+ tab_list = create_tab_list(tab_label);
+
+ /* Set up the tab list scrolled window.
+ *
+ * The following GtkScrolledWindow widget has a hardcoded minimum size,
+ * because there seems to be (in GTK+ versions before 3.22) no way to
+ * set the natural size of GtkScrolledWindow and its GtkViewport.
+ *
+ * See tab-chrome.c for more information. */
+ tab_list_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_min_content_width(
+ GTK_SCROLLED_WINDOW(tab_list_scrolled_window), 400);
+ gtk_scrolled_window_set_min_content_height(
+ GTK_SCROLLED_WINDOW(tab_list_scrolled_window), 200);
+ gtk_container_add(GTK_CONTAINER(tab_list_scrolled_window), tab_list);
+
+ /* Add tab list toggle button handler. */
+ g_signal_connect(buttons[1 * BUTTON_COLS + 3], "toggled",
+ G_CALLBACK(tab_list_button_toggled_cb),
+ tab_list_scrolled_window);
+
+ /* Set up the button rows box. */
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start(GTK_BOX(box), button_grid,
+ TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), tab_list_scrolled_window,
+ TRUE, FALSE, 0);
+
+ /* Set up the popover. */
+ tab_label->popover = gtk_popover_new(widget);
+ gtk_container_add(GTK_CONTAINER(tab_label->popover), box);
+
+ /* NB: gtk_popover_popup() is new in GTK+ 3.22. */
+ gtk_widget_show_all(tab_label->popover);
+ gtk_widget_hide(tab_list_scrolled_window);
+}
+
+#undef BUTTON_ROWS
+#undef BUTTON_COLS
+#undef NEW_BUTTON
+#undef NEW_TOGGLE
+#undef CLICKED_CB
+
static gboolean
button_press_cb(GtkWidget *widget, GdkEventButton *event,
MqTabLabel *tab_label)
{
+ /* Create a popover menu on right click. */
+ if (event->button == 3) {
+ create_tab_popover(widget, tab_label);
+ }
+
return FALSE;
}
@@ -249,7 +464,7 @@ mq_tab_label_init(MqTabLabel *tab_label)
/* Set up event box. */
g_signal_connect(tab_label, "button-press-event",
- G_CALLBACK(button_press_cb), NULL);
+ G_CALLBACK(button_press_cb), tab_label);
gtk_event_box_set_visible_window(GTK_EVENT_BOX(tab_label), FALSE);
gtk_container_add(GTK_CONTAINER(tab_label), box);
}