[gnome-usage] Add Storage Graph Improve generating colors of files.
- From: Petr Štětka <pstetka src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-usage] Add Storage Graph Improve generating colors of files.
- Date: Mon, 6 Mar 2017 14:44:59 +0000 (UTC)
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]