[gnome-boxes/boxes-newbox-assistant-306] Downloads Hub WIP
- From: Felipe Borges <felipeborges src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes/boxes-newbox-assistant-306] Downloads Hub WIP
- Date: Wed, 20 Nov 2019 12:42:20 +0000 (UTC)
commit f6df3b016edf6411a8332389000c3e457eb03307
Author: Felipe Borges <felipeborges gnome org>
Date: Mon Nov 18 16:50:51 2019 +0100
Downloads Hub WIP
data/gnome-boxes.gresource.xml | 2 +
data/ui/assistant/pages/downloads-page.ui | 17 ++++
data/ui/assistant/pages/index-page.ui | 3 +-
data/ui/collection-toolbar.ui | 27 +++++++
data/ui/downloads-hub-row.ui | 70 ++++++++++++++++
data/ui/downloads-hub.ui | 16 ++++
src/app-window.vala | 6 +-
src/app.vala | 10 ++-
src/assistant/downloads-page.vala | 31 +++++--
src/assistant/index-page.vala | 29 ++++---
src/assistant/rhel-download-dialog.vala | 22 ++---
src/assistant/vm-assistant.vala | 28 ++++++-
src/collection-toolbar.vala | 6 +-
src/downloads-hub.vala | 129 ++++++++++++++++++++++++++++++
src/meson.build | 1 +
15 files changed, 355 insertions(+), 42 deletions(-)
---
diff --git a/data/gnome-boxes.gresource.xml b/data/gnome-boxes.gresource.xml
index 67dfbd8f..4db4ad80 100644
--- a/data/gnome-boxes.gresource.xml
+++ b/data/gnome-boxes.gresource.xml
@@ -13,6 +13,8 @@
<file preprocess="xml-stripblanks">ui/collection-toolbar.ui</file>
<file preprocess="xml-stripblanks">ui/display-page.ui</file>
<file preprocess="xml-stripblanks">ui/display-toolbar.ui</file>
+ <file preprocess="xml-stripblanks">ui/downloads-hub.ui</file>
+ <file preprocess="xml-stripblanks">ui/downloads-hub-row.ui</file>
<file preprocess="xml-stripblanks">ui/editable-entry.ui</file>
<file preprocess="xml-stripblanks">ui/empty-boxes.ui</file>
<file preprocess="xml-stripblanks">ui/icon-view.ui</file>
diff --git a/data/ui/assistant/pages/downloads-page.ui b/data/ui/assistant/pages/downloads-page.ui
index 03cf7b3b..ea80b0c4 100644
--- a/data/ui/assistant/pages/downloads-page.ui
+++ b/data/ui/assistant/pages/downloads-page.ui
@@ -2,6 +2,16 @@
<interface>
<!-- interface-requires gtk+ 3.9 -->
<template class="BoxesAssistantDownloadsPage" parent="GtkStack">
+ <signal name="key-press-event" handler="on_key_pressed"/>
+ <child type="title">
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="visible">True</property>
+ <property name="width-chars">50</property>
+ <property name="can-focus">True</property>
+ <property name="placeholder-text" translatable="yes">Search for an OS or enter a download
link…</property>
+ <signal name="search-changed" handler="on_search_changed"/>
+ </object>
+ </child>
<child>
<object class="GtkBox">
@@ -108,4 +118,11 @@
</child>
</template>
+
+ <object class="GtkSearchEntry" id="searchbar">
+ <property name="visible">True</property>
+ <property name="width-chars">50</property>
+ <property name="can-focus">True</property>
+ <property name="placeholder-text" translatable="yes">Search for an OS or enter a download
link…</property>
+ </object>
</interface>
diff --git a/data/ui/assistant/pages/index-page.ui b/data/ui/assistant/pages/index-page.ui
index b400f1ab..cd1380f5 100644
--- a/data/ui/assistant/pages/index-page.ui
+++ b/data/ui/assistant/pages/index-page.ui
@@ -7,6 +7,7 @@
<child>
<object class="GtkStack" id="stack">
<property name="visible">True</property>
+ <signal name="notify::visible-child" handler="update_topbar"/>
<child>
<object class="GtkScrolledWindow" id="home_page">
@@ -271,7 +272,7 @@
<child>
<object class="BoxesAssistantDownloadsPage" id="recommended_downloads_page">
<property name="visible">True</property>
- <signal name="media-selected" handler="on_download_selected"/>
+ <signal name="media-selected" handler="on_featured_media_selected"/>
</object>
</child>
diff --git a/data/ui/collection-toolbar.ui b/data/ui/collection-toolbar.ui
index ad117123..64516535 100644
--- a/data/ui/collection-toolbar.ui
+++ b/data/ui/collection-toolbar.ui
@@ -61,6 +61,33 @@
</object>
</child>
+ <child>
+ <object class="GtkMenuButton" id="downloads_hub_btn">
+ <property name="visible">False</property>
+ <property name="valign">center</property>
+ <property name="use-underline">True</property>
+ <style>
+ <class name="image-button"/>
+ </style>
+ <child internal-child="accessible">
+ <object class="AtkObject">
+ <property name="accessible-name" translatable="yes">Downloads</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">media-record-symbolic</property>
+ <property name="icon-size">1</property>
+ </object>
+ </child>
+ </object>
+
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+
<child>
<object class="GtkMenuButton" id="hamburger_btn">
<property name="visible">True</property>
diff --git a/data/ui/downloads-hub-row.ui b/data/ui/downloads-hub-row.ui
new file mode 100644
index 00000000..daf6a026
--- /dev/null
+++ b/data/ui/downloads-hub-row.ui
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.19"/>
+ <template class="BoxesDownloadsHubRow" parent="GtkListBoxRow">
+ <property name="visible">True</property>
+ <property name="selectable">False</property>
+
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="border-width">10</property>
+ <property name="column-spacing">20</property>
+
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="visible">True</property>
+ <property name="icon-name">media-optical</property>
+ <property name="icon-size">0</property>
+ <property name="pixel-size">64</property>
+ </object>
+ <packing>
+ <property name="height">2</property>
+ <property name="left-attach">0</property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkLabel" id="label">
+ <property name="visible">True</property>
+ <property name="wrap">True</property>
+ <property name="max-width-chars">40</property>
+ <property name="halign">start</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkProgressBar" id="progress_bar">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ </packing>
+ </child>
+
+ <child>
+ <object class="GtkButton">
+ <property name="visible">True</property>
+ <property name="valign">center</property>
+ <signal name="clicked" handler="cancel_download"/>
+
+ <child>
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="icon-name">window-close-symbolic</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/downloads-hub.ui b/data/ui/downloads-hub.ui
new file mode 100644
index 00000000..5d0665c2
--- /dev/null
+++ b/data/ui/downloads-hub.ui
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.19"/>
+ <template class="BoxesDownloadsHub" parent="GtkPopover">
+ <property name="can_focus">False</property>
+ <property name="modal">True</property>
+ <property name="position">bottom</property>
+
+ <child>
+ <object class="GtkListBox" id="listbox">
+ <property name="visible">True</property>
+ <signal name="row-activated" handler="on_row_activated"/>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/app-window.vala b/src/app-window.vala
index 51556afc..46d20ed4 100644
--- a/src/app-window.vala
+++ b/src/app-window.vala
@@ -232,7 +232,7 @@ private void ui_state_changed () {
icon_view,
list_view,
props_window,
- wizard_window,
+ //wizard_window,
empty_boxes }) {
ui.set_state (ui_state);
}
@@ -303,8 +303,8 @@ public void show_remote_connection_assistant () {
new Boxes.RemoteConnectionAssistant (this).run ();
}
- public void show_vm_assistant () {
- new Boxes.VMAssistant (this).run ();
+ public void show_vm_assistant (string? path = null) {
+ new Boxes.VMAssistant (this, path).run ();
}
public void show_properties () {
diff --git a/src/app.vala b/src/app.vala
index c77b5e04..dd6509d8 100644
--- a/src/app.vala
+++ b/src/app.vala
@@ -91,6 +91,10 @@ public App () {
action.activate.connect ((param) => { open_name (param.get_string ()); });
add_action (action);
+ action = new GLib.SimpleAction ("install", GLib.VariantType.STRING);
+ action.activate.connect ((param) => { install (param.get_string ()); });
+ add_action (action);
+
action = new GLib.SimpleAction ("about", null);
action.activate.connect (() => {
string[] authors = {
@@ -313,6 +317,10 @@ public void open_name (string name) {
}
}
+ public void install (string path) {
+ main_window.show_vm_assistant (path);
+ }
+
public bool open_uuid (string uuid) {
main_window.set_state (UIState.COLLECTION);
@@ -450,7 +458,7 @@ private async void setup_default_source () ensures (default_connection != null)
}
}
- private new void send_notification (string notification_id, GLib.Notification notification) {
+ public new void send_notification (string notification_id, GLib.Notification notification) {
base.send_notification (notification_id, notification);
system_notifications.append (notification_id);
diff --git a/src/assistant/downloads-page.vala b/src/assistant/downloads-page.vala
index 8872117b..67d9a2e2 100644
--- a/src/assistant/downloads-page.vala
+++ b/src/assistant/downloads-page.vala
@@ -17,10 +17,12 @@
private Gtk.ListBox listbox;
[GtkChild]
private Gtk.ListBox recommended_listbox;
+ [GtkChild]
+ public Gtk.SearchEntry search_entry;
private GLib.ListStore recommended_model;
- public signal void media_selected (string url);
+ public signal void media_selected (Gtk.ListBoxRow row);
private AssistantDownloadsPageView _page;
public AssistantDownloadsPageView page {
@@ -74,17 +76,12 @@ private async void populate_recommended_list () {
}
private Gtk.Widget create_downloads_entry (Object item) {
- var media = item as Osinfo.Media;
-
- return new WizardDownloadableEntry (media);
+ return new WizardDownloadableEntry (item as Osinfo.Media);
}
[GtkCallback]
private void on_listbox_row_activated (Gtk.ListBoxRow row) {
- // Start to download Entry
- var entry = row as WizardDownloadableEntry;
-
- media_selected (entry.url);
+ media_selected (row);
}
[GtkCallback]
@@ -93,4 +90,22 @@ private void on_show_more_button_clicked () {
page = AssistantDownloadsPageView.SEARCH_RESULTS;
}
+
+ [GtkCallback]
+ private void on_search_changed () {
+ var text = search_entry.get_text ();
+
+ if (text == null)
+ return;
+
+ search.text = text;
+ }
+
+ [GtkCallback]
+ private bool on_key_pressed (Gtk.Widget widget, Gdk.EventKey event) {
+ if (!search_entry.has_focus)
+ search_entry.grab_focus ();
+
+ return search_entry.key_press_event (event);
+ }
}
diff --git a/src/assistant/index-page.vala b/src/assistant/index-page.vala
index 7a39d3df..f0fac237 100644
--- a/src/assistant/index-page.vala
+++ b/src/assistant/index-page.vala
@@ -42,11 +42,13 @@ public void setup (AppWindow app_window, VMAssistant dialog) {
public void go_back () {
if (stack.visible_child == home_page) {
- dialog.close ();
+ dialog.shutdown ();
+
+ return;
}
stack.visible_child = home_page;
- dialog.previous_button.label = _("Cancel");
+ update_topbar ();
}
private async void populate_media_lists () {
@@ -82,6 +84,18 @@ private void populate_detected_sources_list (int? number_of_items = null) {
return new WizardDownloadableEntry (object as Osinfo.Media);
}
+ [GtkCallback]
+ private void update_topbar () {
+ dialog.previous_button.label = _("Cancel");
+
+ var titlebar = dialog.get_titlebar () as Gtk.HeaderBar;
+ if (stack.visible_child == recommended_downloads_page) {
+ titlebar.set_custom_title (recommended_downloads_page.search_entry);
+ } else {
+ titlebar.set_custom_title (null);
+ }
+ }
+
[GtkCallback]
private void on_expand_detected_sources_list () {
populate_detected_sources_list ();
@@ -99,7 +113,7 @@ private void on_featured_media_selected (Gtk.ListBoxRow row) {
var entry = row as WizardDownloadableEntry;
if (entry.os.id.has_prefix ("http://redhat.com/rhel/")) {
- var rhel_dialog = new RHELDownloadDialog (app_window, entry.os);
+ var rhel_dialog = new RHELDownloadDialog (app_window, entry);
rhel_dialog.bind_property ("visible", dialog, "visible", BindingFlags.INVERT_BOOLEAN);
int width, height;
@@ -108,9 +122,9 @@ private void on_featured_media_selected (Gtk.ListBoxRow row) {
rhel_dialog.run ();
} else
- on_download_selected (entry.url);
+ DownloadsHub.get_instance ().add_item (entry);
- dialog.close ();
+ dialog.shutdown ();
}
public override void cleanup () {
@@ -137,9 +151,4 @@ private void on_download_an_os_button_clicked () {
dialog.previous_button.label = _("Previous");
}
-
- [GtkCallback]
- private void on_download_selected (string url) {
- print (url);
- }
}
diff --git a/src/assistant/rhel-download-dialog.vala b/src/assistant/rhel-download-dialog.vala
index 48bd1fb9..6716c51e 100644
--- a/src/assistant/rhel-download-dialog.vala
+++ b/src/assistant/rhel-download-dialog.vala
@@ -17,6 +17,8 @@
private GLib.Cancellable cancellable = new GLib.Cancellable ();
+ private WizardDownloadableEntry entry;
+
construct {
var context = web_view.get_context ();
var language_names = GLib.Intl.get_language_names ();
@@ -31,13 +33,15 @@
});
}
- public RHELDownloadDialog (AppWindow app_window, Osinfo.Os os) {
+ public RHELDownloadDialog (AppWindow app_window, WizardDownloadableEntry entry) {
set_transient_for (app_window);
+ this.entry = entry;
var user_agent = GLib.Uri.escape_string (get_user_agent (), null, false);
var authentication_uri = "https://developers.redhat.com/download-manager/rest/featured/file/rhel" +
"?tag=" + user_agent;
+ var os = entry.os;
is_rhel8 = os.id.has_prefix ("http://redhat.com/rhel/8");
web_view.load_uri (authentication_uri);
@@ -71,19 +75,8 @@ private bool on_decide_policy (WebKit.WebView web_view,
debug ("RHEL ISO download URI: %s", download_uri);
- var soup_download_uri = new Soup.URI (download_uri);
- var download_path = soup_download_uri.get_path ();
-
- // Libsoup is supposed to ensure that the path is at least "/".
- return_val_if_fail (download_path != null, false);
- return_val_if_fail (download_path.length > 0, false);
-
- if (!download_path.has_suffix (".iso")) {
- download_path = is_rhel8 ? "/rhel-8.0-x86_64-dvd.iso" : "/rhel.iso";
- }
-
- var filename = GLib.Path.get_basename (download_path);
- print ("===> %s download_path\n\n", download_path);
+ entry.url = download_uri;
+ DownloadsHub.get_instance ().add_item (entry);
decision.ignore ();
this.close ();
@@ -180,5 +173,6 @@ public override void close () {
cancellable.cancel ();
base.close ();
+ destroy ();
}
}
diff --git a/src/assistant/vm-assistant.vala b/src/assistant/vm-assistant.vala
index 164f1796..05707a0f 100644
--- a/src/assistant/vm-assistant.vala
+++ b/src/assistant/vm-assistant.vala
@@ -36,13 +36,31 @@
use_header_bar = 1;
}
- public VMAssistant (AppWindow app_window) {
+ public VMAssistant (AppWindow app_window, string? path = null) {
set_transient_for (app_window);
// TODO: Make the Assistant independent from window states
app_window.set_state (UIState.WIZARD);
index_page.setup (app_window, this);
+
+ // TODO: Rename this to PATH
+ if (path != null)
+ prepare_for_path.begin (path);
+ }
+
+ private async void prepare_for_path (string path) {
+ var media_manager = MediaManager.get_instance ();
+
+ try {
+ var installer_media = yield media_manager.create_installer_media_for_path (path, null);
+ do_preparation (installer_media);
+ } catch (GLib.Error error) {
+ debug("Failed to analyze installer image: %s", error.message);
+
+ var msg = _("Failed to analyze installer media. Corrupted or incomplete media?");
+ App.app.main_window.notificationbar.display_error (msg);
+ }
}
[GtkCallback]
@@ -114,17 +132,19 @@ private async void do_create (Object object) {
vm_creator.launch_vm (machine);
} catch (GLib.Error error) {
warning ("Failed to create machine: %s", error.message);
+
+ // TODO: launch Notification
}
vm_creator.install_media.clean_up_preparation_cache ();
- close ();
+ shutdown ();
}
- public override void close () {
+ public void shutdown () {
// TODO: Make the Assistant independent from window states
App.app.main_window.set_state (UIState.COLLECTION);
- base.close ();
+ destroy ();
}
}
diff --git a/src/collection-toolbar.vala b/src/collection-toolbar.vala
index 2e337148..a2b2139f 100644
--- a/src/collection-toolbar.vala
+++ b/src/collection-toolbar.vala
@@ -16,6 +16,8 @@
[GtkChild]
private Button new_btn;
[GtkChild]
+ private MenuButton downloads_hub_btn;
+ [GtkChild]
private MenuButton hamburger_btn;
[GtkChild]
private CollectionFilterSwitcher filter_switcher;
@@ -46,6 +48,8 @@ public void setup_ui (AppWindow window) {
var builder = new Builder.from_resource ("/org/gnome/Boxes/ui/menus.ui");
MenuModel menu = (MenuModel) builder.get_object ("app-menu");
hamburger_btn.popover = new Popover.from_model (hamburger_btn, menu);
+
+ downloads_hub_btn.popover = DownloadsHub.get_instance ();
}
public void click_back_button () {
@@ -62,7 +66,7 @@ public void click_search_button () {
[GtkCallback]
private void on_new_vm_btn_clicked () {
- window.set_state (UIState.WIZARD);
+ //window.set_state (UIState.WIZARD);
}
[GtkCallback]
diff --git a/src/downloads-hub.vala b/src/downloads-hub.vala
new file mode 100644
index 00000000..e4740fd1
--- /dev/null
+++ b/src/downloads-hub.vala
@@ -0,0 +1,129 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using Gtk;
+
+[GtkTemplate (ui = "/org/gnome/Boxes/ui/downloads-hub.ui")]
+private class Boxes.DownloadsHub : Gtk.Popover {
+ private static DownloadsHub instance;
+ public static DownloadsHub get_instance () {
+ if (instance == null)
+ instance = new DownloadsHub ();
+
+ return instance;
+ }
+
+ [GtkChild]
+ private ListBox listbox;
+
+ private bool ongoing_downloads {
+ get { return (listbox.get_children ().length () > 0); }
+ }
+
+ // TODO: inhibit suspend
+
+ public void add_item (WizardDownloadableEntry entry) {
+ var row = new DownloadsHubRow.from_entry (entry);
+
+ if (!relative_to.visible)
+ relative_to.visible = true;
+
+ row.destroy.connect (on_row_deleted);
+ row.download_complete.connect (on_download_complete);
+
+ if (!ongoing_downloads) {
+ var reason = _("Downloading media");
+
+ App.app.inhibit (App.app.main_window, null, reason);
+ }
+
+ listbox.prepend (row);
+ }
+
+ private void on_row_deleted () {
+ if (!ongoing_downloads) {
+ // Hide the Downloads Hub when there aren't ongoing downloads
+ relative_to.visible = false;
+ }
+ }
+
+ private void on_download_complete (string label, string path) {
+ var msg = _("“%s“ download complete").printf (label);
+ var notification = new GLib.Notification (msg);
+ notification.add_button (_("Install"), "app.install::" + path);
+
+ App.app.send_notification ("downloaded-" + label, notification);
+
+ if (!ongoing_downloads) {
+ App.app.uninhibit ();
+ }
+ }
+
+ [GtkCallback]
+ private void on_row_activated (Gtk.ListBoxRow _row) {
+ var row = _row as DownloadsHubRow;
+
+ if (row.local_file != null) {
+ App.app.main_window.show_vm_assistant (row.local_file);
+
+ popup ();
+ }
+ }
+}
+
+[GtkTemplate (ui= "/org/gnome/Boxes/ui/downloads-hub-row.ui")]
+private class Boxes.DownloadsHubRow : Gtk.ListBoxRow {
+ [GtkChild]
+ private Label label;
+ [GtkChild]
+ private Image image;
+ [GtkChild]
+ private ProgressBar progress_bar;
+
+ private ActivityProgress progress = new ActivityProgress ();
+ private ulong progress_notify_id;
+
+ private Cancellable cancellable = new Cancellable ();
+
+ public string? local_file;
+
+ public signal void download_complete (string label, string path);
+
+ public DownloadsHubRow.from_entry (WizardDownloadableEntry entry) {
+ label.label = entry.title;
+
+ Downloader.fetch_os_logo.begin (image, entry.os, 64);
+
+ progress_notify_id = progress.notify["progress"].connect (() => {
+ progress_bar.fraction = progress.progress;
+ });
+ progress_bar.fraction = progress.progress = 0;
+
+ var soup_download_uri = new Soup.URI (entry.url);
+ var download_path = soup_download_uri.get_path ();
+
+ var filename = GLib.Path.get_basename (download_path);
+
+ download.begin (entry.url, filename);
+ }
+
+ private async void download (string url, string filename) {
+ try {
+ local_file = yield Downloader.fetch_media (url, filename, progress, cancellable);
+ } catch (IOError.CANCELLED cancel_error) { // We did this, so ignore!
+ } catch (GLib.Error error) {
+ App.app.main_window.notificationbar.display_error (_("Failed to download"));
+
+ warning (error.message);
+ return;
+ }
+
+ download_complete (label.label, local_file);
+ }
+
+ [GtkCallback]
+ private void cancel_download () {
+ progress.disconnect (progress_notify_id);
+ cancellable.cancel ();
+
+ destroy ();
+ }
+}
diff --git a/src/meson.build b/src/meson.build
index 4b741984..c78b8fbd 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -54,6 +54,7 @@ vala_sources = [
'display-page.vala',
'display-toolbar.vala',
'display.vala',
+ 'downloads-hub.vala',
'editable-entry.vala',
'i-properties-provider.vala',
'i-collection-view.vala',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]