1
0
mirror of https://gitlab.gnome.org/GNOME/nautilus synced 2024-06-30 23:46:35 +00:00

network-view: Introduce new view and cells

This is the replacement for the Other Locations.

See https://gitlab.gnome.org/GNOME/nautilus/-/issues/2785
This commit is contained in:
António Fernandes 2024-01-24 20:14:31 +00:00
parent 41b3bed264
commit d1437e9fd6
10 changed files with 666 additions and 0 deletions

View File

@ -43,6 +43,8 @@ src/nautilus-location-entry.c
src/nautilus-main.c
src/nautilus-mime-actions.c
src/nautilus-network-address-bar.c
src/nautilus-network-cell.c
src/nautilus-network-view.c
src/nautilus-new-folder-dialog.c
src/nautilus-operations-ui-manager.c
src/nautilus-pathbar.c
@ -82,6 +84,7 @@ src/resources/ui/nautilus-grid-cell.ui
src/resources/ui/nautilus-history-controls.ui
src/resources/ui/nautilus-name-cell.ui
src/resources/ui/nautilus-network-address-bar.ui
src/resources/ui/nautilus-network-cell.ui
src/resources/ui/nautilus-operations-ui-manager-request-passphrase.ui
src/resources/ui/nautilus-pathbar-context-menu.ui
src/resources/ui/nautilus-preferences-window.ui

View File

@ -167,8 +167,12 @@ libnautilus_sources = [
'nautilus-rename-file-popover.h',
'nautilus-network-address-bar.c',
'nautilus-network-address-bar.h',
'nautilus-network-cell.c',
'nautilus-network-cell.h',
'nautilus-network-directory.c',
'nautilus-network-directory.h',
'nautilus-network-view.c',
'nautilus-network-view.h',
'nautilus-new-folder-dialog.c',
'nautilus-new-folder-dialog.h',
'nautilus-compress-dialog.c',

143
src/nautilus-network-cell.c Normal file
View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2024 The GNOME project contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "nautilus-network-cell.h"
#include <glib/gi18n.h>
#include "nautilus-directory.h"
#include "nautilus-file-utilities.h"
#include "nautilus-scheme.h"
struct _NautilusNetworkCell
{
NautilusViewCell parent_instance;
GSignalGroup *item_signal_group;
GtkWidget *icon;
GtkWidget *target_uri;
GtkWidget *unmount_button;
};
G_DEFINE_TYPE (NautilusNetworkCell, nautilus_network_cell, NAUTILUS_TYPE_VIEW_CELL)
static void
update_labels (NautilusNetworkCell *self)
{
g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
g_return_if_fail (item != NULL);
NautilusFile *file = nautilus_view_item_get_file (item);
g_autofree char *target_uri = nautilus_file_get_activation_uri (file);
if (g_str_has_prefix (target_uri, SCHEME_COMPUTER ":///"))
{
/* Online accounts do not currently have a target URI. */
g_set_str (&target_uri, _("Online Account"));
}
gtk_label_set_label (GTK_LABEL (self->target_uri), target_uri);
}
static void
update_icon (NautilusNetworkCell *self)
{
g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
g_return_if_fail (item != NULL);
g_autoptr (GIcon) icon = nautilus_file_get_gicon (nautilus_view_item_get_file (item),
NAUTILUS_FILE_ICON_FLAGS_NONE);
gtk_image_set_from_gicon (GTK_IMAGE (self->icon), icon);
}
static void
on_file_changed (NautilusNetworkCell *self)
{
g_autoptr (NautilusViewItem) item = nautilus_view_cell_get_item (NAUTILUS_VIEW_CELL (self));
g_return_if_fail (item != NULL);
update_icon (self);
update_labels (self);
NautilusFile *file = nautilus_view_item_get_file (item);
gtk_widget_set_visible (self->unmount_button, nautilus_file_can_unmount (file));
}
static void
on_unmount_clicked (NautilusNetworkCell *self)
{
/* Select item first, because view.unmount-volume acts on selection. */
gtk_widget_activate_action (GTK_WIDGET (self), "listitem.select", "(bb)", FALSE, FALSE);
gtk_widget_activate_action (GTK_WIDGET (self), "view.unmount-volume", NULL);
}
static void
nautilus_network_cell_init (NautilusNetworkCell *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
/* Connect automatically to an item. */
self->item_signal_group = g_signal_group_new (NAUTILUS_TYPE_VIEW_ITEM);
g_signal_group_connect_swapped (self->item_signal_group, "file-changed",
(GCallback) on_file_changed, self);
g_signal_connect_object (self->item_signal_group, "bind",
(GCallback) on_file_changed, self,
G_CONNECT_SWAPPED);
g_object_bind_property (self, "item",
self->item_signal_group, "target",
G_BINDING_SYNC_CREATE);
}
static void
nautilus_network_cell_dispose (GObject *object)
{
NautilusNetworkCell *self = (NautilusNetworkCell *) object;
gtk_widget_dispose_template (GTK_WIDGET (self), NAUTILUS_TYPE_NETWORK_CELL);
G_OBJECT_CLASS (nautilus_network_cell_parent_class)->dispose (object);
}
static void
nautilus_network_cell_finalize (GObject *object)
{
NautilusNetworkCell *self = (NautilusNetworkCell *) object;
g_clear_object (&self->item_signal_group);
G_OBJECT_CLASS (nautilus_network_cell_parent_class)->finalize (object);
}
static void
nautilus_network_cell_class_init (NautilusNetworkCellClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = nautilus_network_cell_dispose;
object_class->finalize = nautilus_network_cell_finalize;
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-network-cell.ui");
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkCell, icon);
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkCell, target_uri);
gtk_widget_class_bind_template_child (widget_class, NautilusNetworkCell, unmount_button);
gtk_widget_class_bind_template_callback (widget_class, on_unmount_clicked);
}
NautilusViewCell *
nautilus_network_cell_new (NautilusListBase *view)
{
return NAUTILUS_VIEW_CELL (g_object_new (NAUTILUS_TYPE_NETWORK_CELL,
"view", view,
NULL));
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2024 António Fernandes <antoniof@gnome.org>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "nautilus-view-cell.h"
G_BEGIN_DECLS
#define NAUTILUS_TYPE_NETWORK_CELL (nautilus_network_cell_get_type())
G_DECLARE_FINAL_TYPE (NautilusNetworkCell, nautilus_network_cell, NAUTILUS, NETWORK_CELL, NautilusViewCell)
NautilusViewCell * nautilus_network_cell_new (NautilusListBase *view);
G_END_DECLS

385
src/nautilus-network-view.c Normal file
View File

@ -0,0 +1,385 @@
/*
* Copyright (C) 2024 The GNOME project contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "nautilus-list-base-private.h"
#include "nautilus-network-view.h"
#include <glib/gi18n.h>
#include "nautilus-global-preferences.h"
#include "nautilus-network-cell.h"
#include "nautilus-scheme.h"
struct _NautilusNetworkView
{
NautilusListBase parent_instance;
GtkListView *view_ui;
};
G_DEFINE_TYPE (NautilusNetworkView, nautilus_network_view, NAUTILUS_TYPE_LIST_BASE)
#define get_view_item(li) \
(NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (GTK_TREE_LIST_ROW (gtk_list_item_get_item (li)))))
static const NautilusViewInfo network_view_info =
{
.view_id = NAUTILUS_VIEW_NETWORK_ID,
.zoom_level_min = NAUTILUS_LIST_ZOOM_LEVEL_SMALL,
.zoom_level_max = NAUTILUS_LIST_ZOOM_LEVEL_SMALL,
.zoom_level_standard = NAUTILUS_LIST_ZOOM_LEVEL_SMALL,
};
static NautilusViewInfo
real_get_view_info (NautilusListBase *list_base)
{
return network_view_info;
}
enum
{
SECTION_CONNECTED,
SECTION_PREVIOUS,
SECTION_AVAILABLE,
};
static inline gint
get_section (NautilusViewItem *item)
{
NautilusFile *file = nautilus_view_item_get_file (item);
if (nautilus_file_can_unmount (file))
{
return SECTION_CONNECTED;
}
g_autoptr (GFile) location = nautilus_file_get_location (file);
if (g_file_has_uri_scheme (location, SCHEME_NETWORK))
{
return SECTION_AVAILABLE;
}
else
{
return SECTION_PREVIOUS;
}
}
static gint
sort_network_sections (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
GtkTreeListRow *row_a = GTK_TREE_LIST_ROW ((gpointer) a);
GtkTreeListRow *row_b = GTK_TREE_LIST_ROW ((gpointer) b);
g_autoptr (NautilusViewItem) item_a = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row_a));
g_autoptr (NautilusViewItem) item_b = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row_b));
return get_section (item_a) - get_section (item_b);
}
static gint
sort_network_items (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
NautilusViewItem *item_a = NAUTILUS_VIEW_ITEM ((gpointer) a);
NautilusViewItem *item_b = NAUTILUS_VIEW_ITEM ((gpointer) b);
NautilusFile *file_a = nautilus_view_item_get_file (item_a);
NautilusFile *file_b = nautilus_view_item_get_file (item_b);
if (get_section (item_a) == SECTION_PREVIOUS &&
get_section (item_a) == SECTION_PREVIOUS)
{
return nautilus_file_compare_for_sort (file_a, file_b,
NAUTILUS_FILE_SORT_BY_ATIME,
FALSE, TRUE /* reversed */);
}
return nautilus_file_compare_for_sort (file_a, file_b,
NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
FALSE, FALSE);
}
static void
real_set_zoom_level (NautilusListBase *list_base,
int new_level)
{
g_warn_if_fail (new_level == network_view_info.zoom_level_standard);
}
/* We only care about the keyboard activation part that GtkListView provides,
* but we don't need any special filtering here. Indeed, we ask GtkListView
* to not activate on single click, and we get to handle double clicks before
* GtkListView does (as one of widget subclassing's goal is to modify the parent
* class's behavior), while claiming the click gestures, so it means GtkListView
* will never react to a click event to emit this signal. So we should be pretty
* safe here with regards to our custom item click handling.
*/
static void
on_list_view_item_activated (GtkListView *list_view,
guint position,
gpointer user_data)
{
NautilusNetworkView *self = NAUTILUS_NETWORK_VIEW (user_data);
nautilus_list_base_activate_selection (NAUTILUS_LIST_BASE (self), FALSE);
}
static guint
real_get_icon_size (NautilusListBase *list_base_view)
{
return NAUTILUS_LIST_ICON_SIZE_SMALL;
}
static GtkWidget *
real_get_view_ui (NautilusListBase *list_base_view)
{
NautilusNetworkView *self = NAUTILUS_NETWORK_VIEW (list_base_view);
return GTK_WIDGET (self->view_ui);
}
static int
real_get_zoom_level (NautilusListBase *list_base_view)
{
return network_view_info.zoom_level_standard;
}
static void
real_scroll_to (NautilusListBase *list_base_view,
guint position,
GtkListScrollFlags flags,
GtkScrollInfo *scroll)
{
NautilusNetworkView *self = NAUTILUS_NETWORK_VIEW (list_base_view);
gtk_list_view_scroll_to (self->view_ui, position, flags, scroll);
}
static GVariant *
real_get_sort_state (NautilusListBase *list_base)
{
return g_variant_take_ref (g_variant_new ("(sb)", "invalid", FALSE));
}
static void
real_set_sort_state (NautilusListBase *list_base,
GVariant *value)
{
/* No op */
}
static void
nautilus_network_view_dispose (GObject *object)
{
G_OBJECT_CLASS (nautilus_network_view_parent_class)->dispose (object);
}
static void
nautilus_network_view_finalize (GObject *object)
{
G_OBJECT_CLASS (nautilus_network_view_parent_class)->finalize (object);
}
static void
bind_cell (GtkSignalListItemFactory *factory,
GtkListItem *listitem,
gpointer user_data)
{
GtkWidget *cell = gtk_list_item_get_child (listitem);
g_autoptr (NautilusViewItem) item = get_view_item (listitem);
nautilus_view_item_set_item_ui (item, cell);
}
static void
unbind_cell (GtkSignalListItemFactory *factory,
GtkListItem *listitem,
gpointer user_data)
{
g_autoptr (NautilusViewItem) item = get_view_item (listitem);
/* item may be NULL when row has just been destroyed. */
if (item != NULL)
{
nautilus_view_item_set_item_ui (item, NULL);
}
}
static void
setup_cell (GtkSignalListItemFactory *factory,
GtkListItem *listitem,
gpointer user_data)
{
NautilusNetworkView *self = NAUTILUS_NETWORK_VIEW (user_data);
NautilusViewCell *cell;
GtkExpression *expression;
cell = nautilus_network_cell_new (NAUTILUS_LIST_BASE (self));
gtk_list_item_set_child (listitem, GTK_WIDGET (cell));
setup_cell_common (G_OBJECT (listitem), cell);
setup_cell_hover (cell);
g_object_bind_property (self, "icon-size",
cell, "icon-size",
G_BINDING_SYNC_CREATE);
/* Use file display name as accessible label. Explaining in pseudo-code:
* listitem:accessible-name :- listitem:item:item:file:display-name */
expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, NULL, "item");
expression = gtk_property_expression_new (GTK_TYPE_TREE_LIST_ROW, expression, "item");
expression = gtk_property_expression_new (NAUTILUS_TYPE_VIEW_ITEM, expression, "file");
expression = gtk_property_expression_new (NAUTILUS_TYPE_FILE, expression, "display-name");
gtk_expression_bind (expression, listitem, "accessible-label", listitem);
}
static void
bind_header (GtkSignalListItemFactory *factory,
GtkListHeader *listheader,
gpointer user_data)
{
GtkWidget *label = gtk_list_header_get_child (listheader);
GtkTreeListRow *row = GTK_TREE_LIST_ROW (gtk_list_header_get_item (listheader));
g_autoptr (NautilusViewItem) item = NAUTILUS_VIEW_ITEM (gtk_tree_list_row_get_item (row));
switch (get_section (item))
{
case SECTION_CONNECTED:
{
/* Translators: This refers to network places which are currently mounted */
gtk_label_set_label (GTK_LABEL (label), _("Connected"));
}
break;
case SECTION_PREVIOUS:
{
/* Translators: This refers to network servers the user has previously connected to */
gtk_label_set_label (GTK_LABEL (label), _("Previous"));
}
break;
case SECTION_AVAILABLE:
{
gtk_label_set_label (GTK_LABEL (label), _("Available on Current Network"));
/* TODO: Use network name from NMClient:primary-connection:id to
* match design mockup: "Available on Network1234"
*/
}
break;
default:
{
g_assert_not_reached ();
}
}
}
static void
setup_header (GtkSignalListItemFactory *factory,
GtkListHeader *listheader,
gpointer user_data)
{
GtkWidget *label = gtk_label_new (NULL);
gtk_widget_add_css_class (label, "heading");
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
gtk_list_header_set_child (listheader, label);
}
static void
on_model_changed (NautilusNetworkView *self)
{
NautilusViewModel *model = nautilus_list_base_get_model (NAUTILUS_LIST_BASE (self));
if (model != NULL)
{
g_autoptr (GtkCustomSorter) sorter = gtk_custom_sorter_new (sort_network_items, self, NULL);
g_autoptr (GtkCustomSorter) sections_sorter = gtk_custom_sorter_new (sort_network_sections, self, NULL);
nautilus_view_model_set_sorter (model, GTK_SORTER (sorter));
nautilus_view_model_set_section_sorter (model, GTK_SORTER (sections_sorter));
}
gtk_list_view_set_model (self->view_ui, GTK_SELECTION_MODEL (model));
}
static GtkListView *
create_view_ui (NautilusNetworkView *self)
{
g_autoptr (GtkListItemFactory) factory = gtk_signal_list_item_factory_new ();
g_autoptr (GtkListItemFactory) header_factory = gtk_signal_list_item_factory_new ();
GtkListView *list_view;
g_signal_connect (factory, "setup", G_CALLBACK (setup_cell), self);
g_signal_connect (factory, "bind", G_CALLBACK (bind_cell), self);
g_signal_connect (factory, "unbind", G_CALLBACK (unbind_cell), self);
list_view = GTK_LIST_VIEW (gtk_list_view_new (NULL, g_steal_pointer (&factory)));
g_signal_connect (header_factory, "setup", G_CALLBACK (setup_header), self);
g_signal_connect (header_factory, "bind", G_CALLBACK (bind_header), self);
gtk_list_view_set_header_factory (list_view, header_factory);
/* We don't use the built-in child activation feature for clicks because it
* doesn't fill all our needs nor does it match our expected behavior.
* Instead, we roll our own event handling and double/single click mode.
* However, GtkListView:single-click-activate has other effects besides
* activation, as it affects the selection behavior as well (e.g. selects on
* hover). Setting it to FALSE gives us the expected behavior. */
gtk_list_view_set_single_click_activate (list_view, FALSE);
gtk_list_view_set_enable_rubberband (list_view, FALSE);
gtk_list_view_set_tab_behavior (list_view, GTK_LIST_TAB_ITEM);
/* While we don't want to use GTK's click activation, we'll let it handle
* the key activation part (with Enter). */
g_signal_connect (list_view, "activate", G_CALLBACK (on_list_view_item_activated), self);
return list_view;
}
static void
nautilus_network_view_class_init (NautilusNetworkViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
NautilusListBaseClass *list_base_view_class = NAUTILUS_LIST_BASE_CLASS (klass);
object_class->dispose = nautilus_network_view_dispose;
object_class->finalize = nautilus_network_view_finalize;
list_base_view_class->get_icon_size = real_get_icon_size;
list_base_view_class->get_sort_state = real_get_sort_state;
list_base_view_class->get_view_info = real_get_view_info;
list_base_view_class->get_view_ui = real_get_view_ui;
list_base_view_class->get_zoom_level = real_get_zoom_level;
list_base_view_class->scroll_to = real_scroll_to;
list_base_view_class->set_sort_state = real_set_sort_state;
list_base_view_class->set_zoom_level = real_set_zoom_level;
}
static void
nautilus_network_view_init (NautilusNetworkView *self)
{
GtkWidget *scrolled_window = nautilus_list_base_get_scrolled_window (NAUTILUS_LIST_BASE (self));
gtk_widget_add_css_class (GTK_WIDGET (self), "nautilus-network-view");
self->view_ui = create_view_ui (self);
nautilus_list_base_setup_gestures (NAUTILUS_LIST_BASE (self));
g_signal_connect_swapped (self, "notify::model", G_CALLBACK (on_model_changed), self);
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_WIDGET (self->view_ui));
nautilus_list_base_set_zoom_level (NAUTILUS_LIST_BASE (self), network_view_info.zoom_level_standard);
}
NautilusNetworkView *
nautilus_network_view_new (void)
{
return g_object_new (NAUTILUS_TYPE_NETWORK_VIEW, NULL);
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2024 The GNOME project contributors
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "nautilus-list-base.h"
G_BEGIN_DECLS
#define NAUTILUS_TYPE_NETWORK_VIEW (nautilus_network_view_get_type())
G_DECLARE_FINAL_TYPE (NautilusNetworkView, nautilus_network_view, NAUTILUS, NETWORK_VIEW, NautilusListBase)
NautilusNetworkView *nautilus_network_view_new (void);
G_END_DECLS

View File

@ -10,6 +10,7 @@
#define NAUTILUS_VIEW_LIST_ID 1
#define NAUTILUS_VIEW_GRID_ID 2
/* Special ids, not used by GSettings schemas: */
#define NAUTILUS_VIEW_NETWORK_ID 3
#define NAUTILUS_VIEW_INVALID_ID 0
typedef struct

View File

@ -28,6 +28,7 @@
<file>ui/nautilus-grid-cell.ui</file>
<file>ui/nautilus-name-cell.ui</file>
<file>ui/nautilus-network-address-bar.ui</file>
<file>ui/nautilus-network-cell.ui</file>
<file alias="gtk/ui/nautilusgtksidebarrow.ui">../gtk/nautilusgtksidebarrow.ui</file>
<file alias="icons/filmholes.png">../../icons/filmholes.png</file>
<file>style.css</file>

View File

@ -162,10 +162,12 @@
/* Setup padding on the list. Horizontal padding must be set on the columnview
* for it to calculate column widths correctly. */
.nautilus-network-view listview,
.nautilus-list-view columnview {
padding-left: 24px;
padding-right: 24px;
}
.nautilus-network-view listview,
.nautilus-list-view columnview > listview {
padding-top: 16px;
padding-bottom: 24px;
@ -187,6 +189,7 @@
margin-right: 24px;
}
.nautilus-network-view listview > row,
.nautilus-list-view columnview > listview > row {
border-radius: 6px;
}
@ -198,6 +201,8 @@
padding: 0px;
}
.nautilus-network-view #NautilusViewCell,
.nautilus-list-view #NautilusViewCell {
padding: 6px;
}
@ -234,6 +239,7 @@
}
/* Both views */
.nautilus-network-view:drop(active),
.nautilus-list-view:drop(active),
.nautilus-grid-view:drop(active) {
box-shadow: none;
@ -242,6 +248,7 @@
/* Remove the default background of the various view widgets
* since we already apply a background to the entire content
* pane with the .view class. Doing this will avoid overdraw. */
.nautilus-network-view listview,
.nautilus-grid-view gridview,
.nautilus-list-view columnview,
placesview list {
@ -270,6 +277,15 @@ placesview list {
filter: drop-shadow(0px 1px 1px rgba(0,0,0,0.3));
}
.nautilus-network-view {
-gtk-icon-style: symbolic;
}
.nautilus-network-view image.network-icon {
padding: 8px;
border-radius: 999999px;
background-color: alpha(currentColor, 0.1);
}
.view .cut {
opacity: 0.55;
}

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<requires lib="gtk" version="4.0"/>
<template class="NautilusNetworkCell" parent="NautilusViewCell">
<accessibility>
<relation name="labelled-by">label</relation>
</accessibility>
<child>
<object class="GtkBox">
<property name="spacing">6</property>
<property name="orientation">horizontal</property>
<child>
<object class="GtkImage" id="icon">
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="network-icon"/>
</style>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="hexpand">true</property>
<property name="valign">center</property>
<child>
<object class="GtkLabel" id="label">
<property name="ellipsize">middle</property>
<property name="max-width-chars">-1</property>
<property name="xalign">0.0</property>
<binding name="label">
<lookup name="display-name">
<lookup name="file">
<lookup name="item">NautilusNetworkCell</lookup>
</lookup>
</lookup>
</binding>
<attributes>
<attribute name="insert-hyphens" value="false"></attribute>
</attributes>
</object>
</child>
<child>
<object class="GtkLabel" id="target_uri">
<property name="ellipsize">middle</property>
<property name="halign">fill</property>
<property name="xalign">0.0</property>
<attributes>
<attribute name="insert-hyphens" value="false"></attribute>
</attributes>
<style>
<class name="caption"/>
<class name="dim-label"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="GtkButton" id="unmount_button">
<property name="tooltip-text" translatable="yes">Disconnect</property>
<property name="visible">False</property>
<property name="icon-name">media-eject-symbolic</property>
<property name="valign">center</property>
<signal name="clicked" object="NautilusNetworkCell" handler="on_unmount_clicked" swapped="yes"/>
<style>
<class name="flat"/>
<class name="circular"/>
</style>
</object>
</child>
</object>
</child>
</template>
</interface>