diff --git a/data/com.github.bleakgrey.tootle.gresource.xml b/data/com.github.bleakgrey.tootle.gresource.xml
index 0dd6a38..11eb9e2 100644
--- a/data/com.github.bleakgrey.tootle.gresource.xml
+++ b/data/com.github.bleakgrey.tootle.gresource.xml
@@ -2,6 +2,7 @@
Application.css
- logo128.png
+ logo128.png
+ empty_state.png
diff --git a/data/empty_state.png b/data/empty_state.png
new file mode 100644
index 0000000..4a0cfcc
Binary files /dev/null and b/data/empty_state.png differ
diff --git a/src/Views/AbstractView.vala b/src/Views/AbstractView.vala
index b7d053c..f9d8a12 100644
--- a/src/Views/AbstractView.vala
+++ b/src/Views/AbstractView.vala
@@ -2,8 +2,9 @@ using Gtk;
public abstract class Tootle.AbstractView : Gtk.ScrolledWindow {
- public Gtk.Image image;
+ public Gtk.Image? image;
public Gtk.Box view;
+ protected Gtk.Box? empty;
construct {
view = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
@@ -40,4 +41,30 @@ public abstract class Tootle.AbstractView : Gtk.ScrolledWindow {
public virtual void bottom_reached (){}
+ public virtual bool is_empty () {
+ return view.get_children ().length () <= 1;
+ }
+
+ public virtual bool empty_state () {
+ if (empty != null)
+ empty.destroy ();
+ if (!is_empty ())
+ return false;
+
+ empty = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ empty.margin = 64;
+ var image = new Image.from_resource ("/com/github/bleakgrey/tootle/empty_state");
+ var text = new Gtk.Label (_("Nothing to see here"));
+ text.get_style_context ().add_class ("h2");
+ text.opacity = 0.5;
+ empty.vexpand = true;
+ empty.valign = Gtk.Align.FILL;
+ empty.pack_start (image, false, false, 0);
+ empty.pack_start (text, false, false, 12);
+ empty.show_all ();
+ view.pack_start (empty, false, false, 0);
+
+ return true;
+ }
+
}
diff --git a/src/Views/AccountView.vala b/src/Views/AccountView.vala
index 9731a2c..b8ab96a 100644
--- a/src/Views/AccountView.vala
+++ b/src/Views/AccountView.vala
@@ -197,6 +197,10 @@ public class Tootle.AccountView : TimelineView {
return btn;
}
+ public override bool is_empty () {
+ return view.get_children ().length () <= 2;
+ }
+
public override bool is_status_owned (Status status){
return status.get_formal ().account.id == account.id;
}
diff --git a/src/Views/AddAccountView.vala b/src/Views/AddAccountView.vala
index 23557ae..1ec348f 100644
--- a/src/Views/AddAccountView.vala
+++ b/src/Views/AddAccountView.vala
@@ -18,7 +18,7 @@ public class Tootle.AddAccountView : Tootle.AbstractView {
hexpand = true;
halign = Gtk.Align.CENTER;
- image = new Image.from_resource ("/com/github/bleakgrey/tootle/logo128.png");
+ image = new Image.from_resource ("/com/github/bleakgrey/tootle/logo128");
image.halign = Gtk.Align.CENTER;
image.hexpand = true;
image.margin_bottom = 24;
diff --git a/src/Views/FollowersView.vala b/src/Views/FollowersView.vala
index 7f2f13f..37c2b54 100644
--- a/src/Views/FollowersView.vala
+++ b/src/Views/FollowersView.vala
@@ -4,10 +4,12 @@ public class Tootle.FollowersView : TimelineView {
public FollowersView (ref Account account) {
base (account.id.to_string ());
-
}
public new void prepend (ref Account account){
+ if (empty != null)
+ empty.destroy ();
+
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
separator.show ();
@@ -27,7 +29,7 @@ public class Tootle.FollowersView : TimelineView {
public override void request (){
var msg = new Soup.Message("GET", get_url ());
- debug (get_url ());
+ msg.finished.connect (() => empty_state ());
Tootle.network.queue(msg, (sess, mess) => {
try{
Tootle.network.parse_array (mess).foreach_element ((array, i, node) => {
diff --git a/src/Views/NotificationsView.vala b/src/Views/NotificationsView.vala
index aebeb57..c668850 100644
--- a/src/Views/NotificationsView.vala
+++ b/src/Views/NotificationsView.vala
@@ -22,7 +22,10 @@ public class Tootle.NotificationsView : AbstractView {
return _("Notifications");
}
- public void prepend (Notification notification, bool invert_order = false) {
+ public void prepend (Notification notification) {
+ if (empty != null)
+ empty.destroy ();
+
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
separator.show ();
@@ -37,8 +40,15 @@ public class Tootle.NotificationsView : AbstractView {
if (!(widget is NotificationWidget))
return;
- if (view.get_children ().length () <= 1)
+ empty_state ();
+ }
+
+ public override bool empty_state () {
+ var is_empty = base.empty_state ();
+ if (image != null && is_empty)
image.icon_name = get_icon ();
+
+ return is_empty;
}
public virtual void on_refresh () {
@@ -89,6 +99,8 @@ public class Tootle.NotificationsView : AbstractView {
warning (e.message);
}
});
+
+ empty_state ();
}
}
diff --git a/src/Views/SearchView.vala b/src/Views/SearchView.vala
index 48013f6..8751cd1 100644
--- a/src/Views/SearchView.vala
+++ b/src/Views/SearchView.vala
@@ -44,12 +44,6 @@ public class Tootle.SearchView : AbstractView {
view.pack_start (widget, false, false, 0);
}
- private void append_empty_state (){
- var empty_state = new Gtk.Label (_("No Results"));
- empty_state.get_style_context ().add_class ("h2");
- view.pack_start (empty_state, false, false, 6);
- }
-
private void append_hashtag (string name) {
var text = "#%s".printf (Tootle.settings.instance_url, Soup.URI.encode (name, null), name);
var widget = new RichLabel (text);
@@ -79,10 +73,6 @@ public class Tootle.SearchView : AbstractView {
var hashtags = root.get_array_member ("hashtags");
clear ();
- if (accounts.get_length () == 0 && statuses.get_length () == 0 && hashtags.get_length () == 0) {
- append_empty_state ();
- return;
- }
if (accounts.get_length () > 0) {
append_header (_("Accounts"));
@@ -108,6 +98,8 @@ public class Tootle.SearchView : AbstractView {
append_hashtag (node.get_string ());
});
}
+
+ empty_state ();
}
catch (GLib.Error e) {
diff --git a/src/Views/TimelineView.vala b/src/Views/TimelineView.vala
index 4c5891f..ee4a59e 100644
--- a/src/Views/TimelineView.vala
+++ b/src/Views/TimelineView.vala
@@ -36,6 +36,9 @@ public class Tootle.TimelineView : AbstractView {
}
public void prepend (ref Status status){
+ if (empty != null)
+ empty.destroy ();
+
var separator = new Gtk.Separator (Gtk.Orientation.HORIZONTAL);
separator.show ();
@@ -58,16 +61,12 @@ public class Tootle.TimelineView : AbstractView {
public virtual void on_remove (Widget widget){
if (!(widget is StatusWidget))
return;
-
- //TODO: empty state
}
public void get_pages (string? header) {
page_next = page_prev = null;
- if (header == null) {
- debug ("No pagingation links");
+ if (header == null)
return;
- }
var pages = header.split (",");
foreach (var page in pages) {
@@ -76,14 +75,10 @@ public class Tootle.TimelineView : AbstractView {
.replace (">", "")
.split (";")[0];
- if ("rel=\"prev\"" in page) {
- debug ("found prev page %s", sanitized);
+ if ("rel=\"prev\"" in page)
page_prev = sanitized;
- }
- else {
- debug ("found next page %s", sanitized);
+ else
page_next = sanitized;
- }
}
is_last_page = page_prev != null & page_next == null;
@@ -100,6 +95,7 @@ public class Tootle.TimelineView : AbstractView {
public virtual void request (){
var msg = new Soup.Message("GET", get_url ());
+ msg.finished.connect (() => empty_state ());
Tootle.network.queue(msg, (sess, mess) => {
try{
Tootle.network.parse_array (mess).foreach_element ((array, i, node) => {
@@ -126,7 +122,6 @@ public class Tootle.TimelineView : AbstractView {
public virtual void on_account_changed (Account? account){
if(account == null)
return;
-
on_refresh ();
}