[gnome-usage] Add Storage Graph Improve generating colors of files.



commit bc96ac7cef5ab4de5302ee5702070cb206aa7953
Author: Petr Štětka <pstetka redhat com>
Date:   Mon Mar 6 15:44:40 2017 +0100

    Add Storage Graph
    Improve generating colors of files.

 data/interface/adwaita-dark.css |   19 ++--
 data/interface/adwaita.css      |   19 ++--
 src/header-bar.vala             |   60 +++++++++---
 src/storage-analyzer.vala       |   43 ++++++--
 src/storage-graph.vala          |  216 +++++++++++++++++++++++++++++++++++++++
 src/storage-list-box.vala       |   46 +++++++-
 src/storage-view.vala           |   38 +++++--
 7 files changed, 376 insertions(+), 65 deletions(-)
---
diff --git a/data/interface/adwaita-dark.css b/data/interface/adwaita-dark.css
index df7a3c0..becb98e 100644
--- a/data/interface/adwaita-dark.css
+++ b/data/interface/adwaita-dark.css
@@ -111,35 +111,30 @@ PieChart.available, ColorRectangle.available {
     color: #232729;
 }
 
-ColorRectangle.system {
+ColorRectangle.system, StorageGraph.system {
     color: #3c4447;
 }
 
-ColorRectangle.applications {
+ColorRectangle.applications, StorageGraph.applications {
     color: #4e9a06;
 }
 
-ColorRectangle.trash {
+ColorRectangle.trash, StorageGraph.trash {
     color: #ef2929;
 }
 
-ColorRectangle.user {
+ColorRectangle.user, StorageGraph.user {
     color: #ad7fa8;
 }
 
-ColorRectangle.available-storage {
+ColorRectangle.available-storage, StorageGraph.available-storage {
     color: #d3d7cf;
 }
 
-row.folders {
+row.folders, list.folders {
     color: #457fd3;
 }
 
-list.folders {
-    color: #457fd3;
-}
-
-box.storage
-{
+box.storage {
     background-color: #232729
 }
\ No newline at end of file
diff --git a/data/interface/adwaita.css b/data/interface/adwaita.css
index f2b6eec..79521c7 100644
--- a/data/interface/adwaita.css
+++ b/data/interface/adwaita.css
@@ -117,35 +117,30 @@ PieChart.available, ColorRectangle.available {
     color: #fafafa;
 }
 
-ColorRectangle.system {
+ColorRectangle.system, StorageGraph.system {
     color: #2e3436;
 }
 
-ColorRectangle.applications {
+ColorRectangle.applications, StorageGraph.applications {
     color: #4e9a06;
 }
 
-ColorRectangle.trash {
+ColorRectangle.trash, StorageGraph.trash {
     color: #ef2929;
 }
 
-ColorRectangle.user {
+ColorRectangle.user, StorageGraph.user {
     color: #ad7fa8;
 }
 
-ColorRectangle.available-storage {
+ColorRectangle.available-storage, StorageGraph.available-storage {
     color: #d3d7cf;
 }
 
-row.folders {
+row.folders, list.folders {
     color: #457fd3;
 }
 
-list.folders {
-    color: #457fd3;
-}
-
-box.storage
-{
+box.storage {
     background-color: #ffffff
 }
\ No newline at end of file
diff --git a/src/header-bar.vala b/src/header-bar.vala
index f68a40a..570e3e7 100644
--- a/src/header-bar.vala
+++ b/src/header-bar.vala
@@ -13,6 +13,8 @@ namespace Usage
            private Gtk.StackSwitcher stack_switcher;
            private Gtk.Button? storage_back_button;
            private bool show_storage_back_btn = false;
+           private bool show_storage_rescan_btn = false;
+           private string title_text = "";
            private Gtk.Button? storage_rescan_button;
            private HeaderBarMode mode;
 
@@ -55,18 +57,26 @@ namespace Usage
                     show_stack_switcher();
                     break;
                 case HeaderBarMode.STORAGE:
-                    show_stack_switcher();
+                    if(title_text == "")
+                        show_stack_switcher();
+                    else
+                        show_title();
                     storage_back_button = new Gtk.Button.from_icon_name("go-previous-symbolic");
                     storage_back_button.clicked.connect(() => {
                         ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().on_back_button_clicked();
                     });
                     show_storage_back_button(show_storage_back_btn);
+
                     storage_rescan_button = new Gtk.Button.from_icon_name("view-refresh-symbolic");
                     storage_rescan_button.clicked.connect(() => {
+                        show_stack_switcher();
+                        show_storage_rescan_button(false);
+                        show_storage_back_button(false);
                         (GLib.Application.get_default() as 
Application).get_storage_analyzer().create_cache.begin(true);
                         ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box().reload();
                     });
-                    storage_rescan_button.show();
+                    show_storage_rescan_button(show_storage_rescan_btn);
+
                     pack_start(storage_back_button);
                     pack_end(storage_rescan_button);
                     break;
@@ -77,12 +87,22 @@ namespace Usage
             this.mode = mode;
            }
 
-           public void show_title_text(string title)
+           public HeaderBarMode get_mode()
+           {
+               return mode;
+           }
+
+           public void show_title()
            {
                set_custom_title(null);
-            set_title(title);
+            set_title(title_text);
            }
 
+           public void set_title_text(string title)
+        {
+            this.title_text = title;
+        }
+
            public void show_stack_switcher()
         {
             set_custom_title(stack_switcher);
@@ -90,20 +110,34 @@ namespace Usage
 
         public void show_storage_back_button(bool show)
         {
-            if(storage_back_button != null)
+            if(show)
             {
-                if(show)
-                {
+                if(storage_back_button != null)
                     storage_back_button.show();
-                    show_storage_back_btn = true;
-                }
-                else
-                {
+                show_storage_back_btn = true;
+            }
+            else
+            {
+                if(storage_back_button != null)
                     storage_back_button.hide();
-                    show_storage_back_btn = false;
-                }
+                show_storage_back_btn = false;
             }
+        }
 
+        public void show_storage_rescan_button(bool show)
+        {
+            if(show)
+            {
+                if(storage_rescan_button != null)
+                    storage_rescan_button.show();
+                show_storage_rescan_btn = true;
+            }
+            else
+            {
+                if(storage_rescan_button != null)
+                    storage_rescan_button.hide();
+                show_storage_rescan_btn = false;
+            }
         }
        }
 }
\ No newline at end of file
diff --git a/src/storage-analyzer.vala b/src/storage-analyzer.vala
index ff31fb8..9e84956 100644
--- a/src/storage-analyzer.vala
+++ b/src/storage-analyzer.vala
@@ -6,14 +6,15 @@ namespace Usage
     {
         public signal void cache_complete();
         public bool cache { private set; get; }
-        public bool separate_home = false;
 
+        private bool separate_home = false;
         private Cancellable cancellable;
         private HashTable<string, Directory?> directory_table;
         private HashTable<string, Storage?> storages;
         private AsyncQueue<StorageResult> results_queue;
         private string[] exclude_from_home;
         private static bool path_null;
+        private bool can_scan = true;
 
         private struct Directory
         {
@@ -45,6 +46,11 @@ namespace Usage
             cancellable.cancel();
         }
 
+        public bool get_separate_home()
+        {
+            return separate_home;
+        }
+
         public async List<StorageItem> prepare_items(string? path, Gdk.RGBA color)
         {
             if(path == null)
@@ -144,7 +150,14 @@ namespace Usage
 
         private void set_colors(ref List<StorageItem> items, Gdk.RGBA default_color)
         {
-            for(int i = 0; i < items.length(); i++)
+            uint showed_items_length = 1;
+            foreach(StorageItem item in items)
+            {
+                if(item.get_percentage() > StorageGraph.MIN_PERCENTAGE_SHOWN_FILES)
+                    showed_items_length++;
+            }
+
+            for(uint i = 0; i < items.length(); i++)
             {
                 unowned StorageItem item = items.nth_data(i);
                 switch(item.get_item_type())
@@ -155,18 +168,22 @@ namespace Usage
                     case StorageItemType.MUSIC:
                     case StorageItemType.PICTURES:
                     case StorageItemType.VIDEOS:
-                        item.set_color(generate_color(default_color, i-2, 6, false)); //6 because DOCUMENTS, 
DOWNLOADS, DESKTOP, MUSIC, PICTURES, VIDEOS, 2 because header and Home
+                        //6 because DOCUMENTS, DOWNLOADS, DESKTOP, MUSIC, PICTURES, VIDEOS, 2 because header 
and Home
+                        item.set_color(generate_color(default_color, i-2, 6, false));
                         break;
                     case StorageItemType.DIRECTORY:
                     case StorageItemType.FILE:
-                        item.set_color(generate_color(default_color, i, items.length(), true));
+                        item.set_color(generate_color(default_color, i, showed_items_length, true));
                         break;
                 }
             }
         }
 
-        private Gdk.RGBA generate_color(Gdk.RGBA default_color, int order, uint all_color, bool reverse = 
false)
+        private Gdk.RGBA generate_color(Gdk.RGBA default_color, uint order, uint all_color, bool reverse = 
false)
         {
+            if(order >= all_color)
+                order = all_color - 1;
+
             order += 1;
 
             double step = 100 / all_color;
@@ -200,12 +217,15 @@ namespace Usage
 
         public async void create_cache(bool force_override = false)
         {
-            if(force_override == true)
-               cache = false;
+            bool scan = false;
+            if(force_override == true || cache == false)
+               scan = true;
 
-            if(cache == false)
+            if(scan && can_scan)
             {
-                cache = true;
+                cache = false;
+                can_scan = false;
+
                 analyze_storages();
                 stop_scanning();
                 cancellable.reset();
@@ -221,8 +241,11 @@ namespace Usage
                 var thread = new Thread<void*>("storage_analyzer", run);
                 yield;
                 thread.join();
+
+                cache = true;
                 cache_complete();
             }
+            can_scan = true;
         }
 
         private void scan_cache()
@@ -279,7 +302,7 @@ namespace Usage
 
         private void add_root_items(ref List<StorageItem> items, Storage storage, int section)
         {
-            items.insert_sorted(new StorageItem.system(_("Operation System"), storage.used, ((float) 
storage.free / storage.total) * 100, section), (CompareFunc) sort);
+            items.insert_sorted(new StorageItem.system(_("Operating System"), storage.used, ((float) 
storage.used / storage.total) * 100, section), (CompareFunc) sort);
         }
 
         private void add_home_items(ref List<StorageItem> items, int section)
diff --git a/src/storage-graph.vala b/src/storage-graph.vala
index 4bc6a25..346265a 100644
--- a/src/storage-graph.vala
+++ b/src/storage-graph.vala
@@ -4,9 +4,225 @@ namespace Usage
 {
     public class StorageGraph : Gtk.DrawingArea
     {
+        public const uint MIN_PERCENTAGE_SHOWN_FILES = 2;
+
+        class construct
+        {
+            set_css_name("StorageGraph");
+        }
+
         public StorageGraph()
         {
+            this.draw.connect(draw_storage_graph);
+        }
+
+        public enum Circle
+        {
+            HOME,
+            ROOT,
+            BASE
+        }
+
+        private void draw_circle(Cairo.Context context, GLib.ListStore model, double x, double y, double 
radius, int section, Circle circle)
+        {
+            double start_angle = 0;
+            double final_angle = - Math.PI / 2.0;
+            double ratio = 0;
+            var fill_color = Gdk.RGBA();
+            var background_color = 
get_toplevel().get_style_context().get_background_color(get_toplevel().get_style_context().get_state());
+
+            for(int i = 0; i < model.get_n_items(); i++)
+            {
+                StorageItem item = (StorageItem) model.get_item(i);
+                if(item.get_percentage() > 0 && item.get_item_type() != StorageItemType.STORAGE && 
item.get_section() == section)
+                {
+                    var style_context = get_style_context();
+                    switch(item.get_item_type())
+                    {
+                        case StorageItemType.SYSTEM:
+                            style_context.add_class("system");
+                            fill_color = style_context.get_color(style_context.get_state());
+                            style_context.remove_class("system");
+                            break;
+                        case StorageItemType.TRASH:
+                            style_context.add_class("trash");
+                            fill_color = style_context.get_color(style_context.get_state());
+                            style_context.remove_class("trash");
+                            break;
+                        case StorageItemType.USER:
+                            style_context.add_class("user");
+                            fill_color = style_context.get_color(style_context.get_state());
+                            style_context.remove_class("user");
+                            break;
+                        case StorageItemType.AVAILABLE:
+                            style_context.add_class("available-storage");
+                            fill_color = style_context.get_color(style_context.get_state());
+                            style_context.remove_class("available-storage");
+                            break;
+                        case StorageItemType.DOCUMENTS:
+                        case StorageItemType.DOWNLOADS:
+                        case StorageItemType.DESKTOP:
+                        case StorageItemType.MUSIC:
+                        case StorageItemType.PICTURES:
+                        case StorageItemType.VIDEOS:
+                        case StorageItemType.DIRECTORY:
+                        case StorageItemType.FILE:
+                            fill_color = item.get_color();
+                            break;
+                    }
+                    context.set_line_width (2.0);
+                    start_angle = final_angle;
+                    ratio = ratio + ((double) item.get_percentage() / 100);
+                    final_angle = ratio * 2 * Math.PI - Math.PI / 2.0;
+                    context.move_to (x, y);
+                    Gdk.cairo_set_source_rgba (context, fill_color);
+                    context.arc (x, y, radius, start_angle, final_angle);
+                    if(item.get_percentage() == 100)
+                        context.fill();
+                    else
+                        context.fill_preserve();
+                    Gdk.cairo_set_source_rgba (context, background_color);
+                    context.stroke();
+
+                    if(item.get_percentage() > MIN_PERCENTAGE_SHOWN_FILES)
+                    {
+                        double midle_angle = start_angle + (final_angle - start_angle) / 2;
+                        midle_angle += Math.PI / 2;
+                        double distance = radius + radius/10;
+                        double x_text = 0;
+                        double y_text = 0;
+                        CornerType quadrant = 0;
+
+                        if(midle_angle <= Math.PI / 2)
+                        {
+                            x_text = Math.sin(midle_angle) * distance;
+                            x_text += x;
+                            y_text = Math.cos(midle_angle) * distance;
+                            y_text = y - y_text;
+                            quadrant = CornerType.TOP_RIGHT;
+                        }
+                        else if (midle_angle <= Math.PI)
+                        {
+                            midle_angle = Math.PI - midle_angle;
+                            x_text = Math.sin(midle_angle) * distance;
+                            x_text += x;
+                            y_text = Math.cos(midle_angle) * distance;
+                            y_text += y;
+                            quadrant = CornerType.BOTTOM_RIGHT;
+                        }
+                        else if (midle_angle <= Math.PI + Math.PI / 2)
+                        {
+                            midle_angle = midle_angle - Math.PI;
+                            x_text = Math.sin(midle_angle) * distance;
+                            x_text = x - x_text;
+                            y_text = Math.cos(midle_angle) * distance;
+                            y_text += y;
+                            quadrant = CornerType.BOTTOM_LEFT;
+                        }
+                        else
+                        {
+                            midle_angle = Math.PI * 2 - midle_angle;
+                            x_text = Math.sin(midle_angle) * distance;
+                            x_text = x - x_text;
+                            y_text = Math.cos(midle_angle) * distance;
+                            y_text = y - y_text;
+                            quadrant = CornerType.TOP_LEFT;
+                        }
+
+                        double space = 0;
+                        const int SIDE_MARGIN = 5;
+
+                        switch(circle)
+                        {
+                            case Circle.HOME:
+                            case Circle.ROOT:
+                                space = get_allocated_width ();
+                                space -= x + distance;
+                                break;
+                            case Circle.BASE:
+                                space = get_allocated_width () / 2;
+                                space -= radius + (distance-radius);
+                                break;
+                        }
+                        space -= SIDE_MARGIN;
+                        x_text += SIDE_MARGIN;
+                        draw_text(context, item.get_name(), x_text, y_text, quadrant, space);
+                    }
+                }
+            }
+        }
+
+        private void draw_text(Cairo.Context context, string text, double x, double y, CornerType 
start_corner, double width)
+        {
+            var layout = create_pango_layout (null);
+            var markup = "<span font='10'>" + text + "</span>";
+            layout.set_markup (markup, -1);
+            layout.set_width ((int) (Pango.SCALE * width));
+            layout.set_ellipsize (Pango.EllipsizeMode.END);
+            Pango.Rectangle layout_rect;
+            layout.get_pixel_extents (null, out layout_rect);
+
+            switch(start_corner)
+            {
+                case CornerType.TOP_RIGHT:
+                    y -= layout_rect.height;
+                    break;
+                case CornerType.BOTTOM_LEFT:
+                    x -= layout_rect.width;
+                    break;
+                case CornerType.TOP_LEFT:
+                    x -= layout_rect.width;
+                    y -= layout_rect.height;
+                    break;
+            }
+
+            get_style_context().render_layout (context, x, y, layout);
+        }
+
+        private bool draw_storage_graph(Cairo.Context context)
+        {
+            int height = this.get_allocated_height ();
+            int width = this.get_allocated_width ();
+
+            var storage_list_box = ((StorageView) (GLib.Application.get_default() as 
Application).get_window().get_views()[2]).get_storage_list_box();
+            var model = storage_list_box.get_model();
+
+            var two_graphs = false;
+            if(storage_list_box.get_root() && (GLib.Application.get_default() as 
Application).get_storage_analyzer().get_separate_home())
+                two_graphs = true;
+
+            double x = 0;
+            double y = 0;
+            double radius = 0;
+            if(two_graphs)
+            {
+                double border = 5.5;
+                radius = int.min (width, height) / 3.5;
+                x = width / 1.8;
+                x -= x / border;
+                y = height / 2.1;
+                y -= y / border;
+                draw_circle(context, model, x, y, radius, 0, Circle.HOME);
+
+                border = 1.75;
+                radius = int.min (width, height) / 11.0;
+                x = width / 2.0;
+                x += x / border;
+                y = height / 1.9;
+                y += y / border;
+                draw_circle(context, model, x, y, radius, 1, Circle.ROOT);
+            }
+            else
+            {
+                radius = int.min (width, height) / 2.0;
+                radius -= radius / 3;
+                x = width / 2.0;
+                y = height / 2.0;
+
+                draw_circle(context, model, x, y, radius, 0, Circle.BASE);
+            }
 
+            return true;
         }
     }
 }
diff --git a/src/storage-list-box.vala b/src/storage-list-box.vala
index bcedbae..0db560b 100644
--- a/src/storage-list-box.vala
+++ b/src/storage-list-box.vala
@@ -4,6 +4,7 @@ namespace Usage
     {
         public signal void loading();
         public signal void loaded();
+        public signal void empty();
 
         private List<string?> path_history;
         private List<string?> name_history;
@@ -29,9 +30,23 @@ namespace Usage
             actual_path = null;
             actual_name = null;
             storage_analyzer = (GLib.Application.get_default() as Application).get_storage_analyzer();
+
+            get_style_context().add_class("folders");
+            color = get_style_context().get_color(get_style_context().get_state());
+            get_style_context().remove_class("folders");
             reload();
         }
 
+        public ListStore get_model()
+        {
+            return model;
+        }
+
+        public bool get_root()
+        {
+            return root;
+        }
+
         public void on_back_button_clicked()
         {
             unowned List<string>? path = path_history.last();
@@ -44,28 +59,43 @@ namespace Usage
             if(root)
             {
                 (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_back_button(false);
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().set_title_text("");
                 (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_stack_switcher();
             }
             else
-                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_title_text(actual_name);
+            {
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().set_title_text(actual_name);
+                (GLib.Application.get_default() as Application).get_window().get_header_bar().show_title();
+            }
         }
 
         public void reload()
         {
-            get_style_context().add_class("folders");
-            color = get_style_context().get_color(get_style_context().get_state());
-            get_style_context().remove_class("folders");
             this.hide();
             loading();
-            root = true;
             storage_analyzer.cache_complete.connect(() => {
                 storage_analyzer.prepare_items.begin(actual_path, color, (obj, res) => {
+                    var header_bar = (GLib.Application.get_default() as 
Application).get_window().get_header_bar();
+                    if(root == false)
+                    {
+                        header_bar.show_storage_back_button(true);
+                        if(header_bar.get_mode() == HeaderBarMode.STORAGE)
+                        {
+                            header_bar.set_title_text(actual_name);
+                            header_bar.show_title();
+                        }
+                    }
+
+                    header_bar.show_storage_rescan_button(true);
                     loaded();
                     this.show();
                     model.remove_all();
 
                     foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
                         model.append(item);
+
+                    if(model.get_n_items() == 0)
+                        empty();
                 });
             });
         }
@@ -91,6 +121,9 @@ namespace Usage
 
                 foreach(unowned StorageItem item in storage_analyzer.prepare_items.end(res))
                     model.append(item);
+
+                if(model.get_n_items() == 0)
+                    empty();
             });
         }
 
@@ -108,7 +141,8 @@ namespace Usage
                 actual_name = storage_row.get_item_name();
 
                 (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_storage_back_button(true);
-                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().show_title_text(actual_name);
+                (GLib.Application.get_default() as 
Application).get_window().get_header_bar().set_title_text(actual_name);
+                (GLib.Application.get_default() as Application).get_window().get_header_bar().show_title();
 
                 if(root)
                     color = storage_row.get_color();
diff --git a/src/storage-view.vala b/src/storage-view.vala
index 9dd4b98..bdc3a12 100644
--- a/src/storage-view.vala
+++ b/src/storage-view.vala
@@ -9,36 +9,50 @@ namespace Usage
             name = "STORAGE";
             title = _("Storage");
 
-            var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
-            box.get_style_context().add_class("storage");
             storage_list_box = new StorageListBox();
             var scrolled_window = new Gtk.ScrolledWindow(null, null);
-            scrolled_window.add(box);
+            scrolled_window.add(storage_list_box);
 
             var spinner = new Gtk.Spinner();
             spinner.active = true;
             spinner.margin_top = 30;
             spinner.margin_bottom = 20;
 
-            box.pack_start(spinner, true, true, 0);
-            box.pack_start(storage_list_box, false, false, 0);
+            var graph = new StorageGraph();
+            var paned = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
+            paned.position = 300;
+            paned.add2(spinner);
+
+            Gtk.Label empty_label = new Gtk.Label("<span font_desc=\"17.0\">" + _("No content here") + 
"</span>");
+            empty_label.set_use_markup (true);
 
             storage_list_box.loading.connect(() =>
             {
-                spinner.show();
+                paned.remove(empty_label);
+                paned.remove(scrolled_window);
+                paned.remove(graph);
+                paned.add2(spinner);
             });
 
             storage_list_box.loaded.connect(() =>
             {
-                spinner.hide();
+                paned.add1(scrolled_window);
+                scrolled_window.show();
+
+                paned.remove(spinner);
+                paned.add2(graph);
+                graph.show();
             });
 
-            var graph = new StorageGraph();
+            storage_list_box.empty.connect(() =>
+            {
+                paned.remove(scrolled_window);
+                paned.remove(graph);
+                paned.remove(spinner);
+                paned.add2(empty_label);
+                empty_label.show();
+            });
 
-            var paned = new Gtk.Paned(Gtk.Orientation.HORIZONTAL);
-            paned.position = 300;
-            paned.add1(scrolled_window);
-            paned.add(graph);
             add(paned);
         }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]