[gitg/wip/actions: 34/34] Implement history navigation as ListBox
- From: Jesse van den Kieboom <jessevdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gitg/wip/actions: 34/34] Implement history navigation as ListBox
- Date: Tue, 14 Jan 2014 23:45:49 +0000 (UTC)
commit 08df00fc3bb6e520d3ce813ab03108d09371ef74
Author: Jesse van den Kieboom <jessevdk gmail com>
Date: Wed Jan 15 00:45:04 2014 +0100
Implement history navigation as ListBox
gitg/Makefile.am | 2 +-
gitg/history/gitg-history-navigation.vala | 581 -----------------------------
gitg/history/gitg-history-paned.vala | 6 +-
gitg/history/gitg-history-refs-list.vala | 418 +++++++++++++++++++++
gitg/history/gitg-history.vala | 105 +++---
gitg/resources/gitg-resources.xml | 1 +
gitg/resources/ui/gitg-history-paned.ui | 4 +-
gitg/resources/ui/gitg-history-ref-row.ui | 24 ++
8 files changed, 491 insertions(+), 650 deletions(-)
---
diff --git a/gitg/Makefile.am b/gitg/Makefile.am
index 8cbc485..35eeaa7 100644
--- a/gitg/Makefile.am
+++ b/gitg/Makefile.am
@@ -64,7 +64,7 @@ gitg_gitg_VALASOURCES = \
gitg/preferences/gitg-preferences-interface.vala \
gitg/preferences/gitg-preferences-history.vala \
gitg/history/gitg-history.vala \
- gitg/history/gitg-history-navigation.vala \
+ gitg/history/gitg-history-refs-list.vala \
gitg/history/gitg-history-paned.vala \
gitg/commit/gitg-commit.vala \
gitg/commit/gitg-commit-paned.vala \
diff --git a/gitg/history/gitg-history-paned.vala b/gitg/history/gitg-history-paned.vala
index 79a1d52..81951a8 100644
--- a/gitg/history/gitg-history-paned.vala
+++ b/gitg/history/gitg-history-paned.vala
@@ -33,7 +33,7 @@ class Paned : Gtk.Paned
private Gtk.StackSwitcher d_stack_switcher_panels;
[GtkChild]
- private NavigationView d_navigation_view;
+ private RefsList d_refs_list;
[GtkChild]
private Gtk.TreeView d_commit_list_view;
@@ -117,9 +117,9 @@ class Paned : Gtk.Paned
Object(orientation: Gtk.Orientation.HORIZONTAL);
}
- public NavigationView navigation_view
+ public RefsList refs_list
{
- get { return d_navigation_view; }
+ get { return d_refs_list; }
}
public Gtk.TreeView commit_list_view
diff --git a/gitg/history/gitg-history-refs-list.vala b/gitg/history/gitg-history-refs-list.vala
new file mode 100644
index 0000000..95c2e11
--- /dev/null
+++ b/gitg/history/gitg-history-refs-list.vala
@@ -0,0 +1,418 @@
+/*
+ * This file is part of gitg
+ *
+ * Copyright (C) 2014 - Jesse van den Kieboom
+ *
+ * gitg is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * gitg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gitg. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GitgHistory
+{
+
+private int ref_type_sort_order(Gitg.RefType ref_type)
+{
+ switch (ref_type)
+ {
+ case Gitg.RefType.NONE:
+ return 0;
+ case Gitg.RefType.BRANCH:
+ return 1;
+ case Gitg.RefType.REMOTE:
+ return 2;
+ case Gitg.RefType.TAG:
+ return 3;
+ }
+
+ return 4;
+}
+
+private interface RefTyped : Object
+{
+ public abstract Gitg.RefType ref_type { get; }
+}
+
+[GtkTemplate (ui = "/org/gnome/gitg/ui/gitg-history-ref-row.ui")]
+private class RefRow : RefTyped, Gtk.Box
+{
+ private const string version = Gitg.Config.VERSION;
+
+ [GtkChild]
+ private Gtk.Image d_icon;
+
+ [GtkChild]
+ private Gtk.Label d_label;
+
+ public Gitg.Ref? reference { get; set; }
+
+ public Gitg.RefType ref_type
+ {
+ get { return reference != null ? reference.parsed_name.rtype : Gitg.RefType.NONE; }
+ }
+
+ public RefRow(Gitg.Ref? reference)
+ {
+ Object(orientation: Gtk.Orientation.HORIZONTAL, spacing: 6);
+
+ this.reference = reference;
+
+ d_label.label = label_text();
+
+ if (is_head)
+ {
+ d_icon.icon_name = "object-select-symbolic";
+ d_icon.show();
+ }
+
+ if (reference != null)
+ {
+ margin_left += 12;
+ }
+
+ if (ref_type == Gitg.RefType.REMOTE)
+ {
+ margin_left += 12;
+ }
+ }
+
+ private string label_text()
+ {
+ if (reference == null)
+ {
+ return _("All commits");
+ }
+
+ var pn = reference.parsed_name;
+
+ if (pn.rtype == Gitg.RefType.REMOTE)
+ {
+ return pn.remote_branch;
+ }
+
+ return pn.shortname;
+ }
+
+ private bool is_head
+ {
+ get
+ {
+ if (reference == null)
+ {
+ return false;
+ }
+
+ var branch = reference as Ggit.Branch;
+
+ if (branch != null)
+ {
+ try
+ {
+ return branch.is_head();
+ } catch { return false; }
+ }
+
+ return false;
+ }
+ }
+
+ private int compare_type(RefRow other)
+ {
+ var pnme = reference.parsed_name;
+ var pnot = other.reference.parsed_name;
+
+ if (pnme.rtype != pnot.rtype)
+ {
+ var i1 = ref_type_sort_order(pnme.rtype);
+ var i2 = ref_type_sort_order(pnot.rtype);
+
+ return i1 < i2 ? -1 : (i1 > i2 ? 1 : 0);
+ }
+
+ if (pnme.rtype == Gitg.RefType.REMOTE)
+ {
+ return pnme.remote_name.casefold().collate(pnot.remote_name.casefold());
+ }
+
+ return 0;
+ }
+
+ public int compare_to(RefRow other)
+ {
+ if (reference == null)
+ {
+ return -1;
+ }
+
+ if (other.reference == null)
+ {
+ return 1;
+ }
+
+ var ct = compare_type(other);
+
+ if (ct != 0)
+ {
+ return ct;
+ }
+
+ var t1 = label_text();
+ var t2 = other.label_text();
+
+ var hassep1 = t1.index_of_char('/');
+ var hassep2 = t2.index_of_char('/');
+
+ if ((hassep1 >= 0) != (hassep2 >= 0))
+ {
+ return hassep1 >= 0 ? 1 : -1;
+ }
+
+ return t1.casefold().collate(t2.casefold());
+ }
+}
+
+private class RefHeader : RefTyped, Gtk.Label
+{
+ private Gitg.RefType d_rtype;
+ private bool d_is_sub_header_remote;
+ private string d_name;
+
+ public Gitg.RefType ref_type
+ {
+ get { return d_rtype; }
+ }
+
+ public RefHeader(Gitg.RefType rtype, string name)
+ {
+ var escaped = Markup.escape_text(name);
+
+ set_markup(@"<b>$escaped</b>");
+ xalign = 0;
+
+ d_name = name;
+ d_rtype = rtype;
+
+ margin_top = 3;
+ margin_bottom = 3;
+ margin_left = 16;
+ }
+
+ public RefHeader.remote(string name)
+ {
+ this(Gitg.RefType.REMOTE, name);
+ d_is_sub_header_remote = true;
+ margin_left += 12;
+ }
+
+ public int compare_to(RefHeader other)
+ {
+ // Both are headers of remote type
+ if (d_is_sub_header_remote != other.d_is_sub_header_remote)
+ {
+ return d_is_sub_header_remote ? 1 : -1;
+ }
+
+ return d_name.casefold().collate(other.d_name.casefold());
+ }
+}
+
+public class RefsList : Gtk.ListBox
+{
+ public signal void edited(Gitg.Ref reference, Gtk.TreePath path, string text);
+ public signal void ref_activated(Gitg.Ref? r);
+
+ private Gitg.Repository? d_repository;
+ private Gee.HashMap<string, RefHeader> d_remote_headers;
+
+ public Gitg.Repository? repository
+ {
+ get { return d_repository; }
+ set
+ {
+ if (d_repository != value)
+ {
+ d_repository = value;
+ }
+
+ refresh();
+ }
+ }
+
+ construct
+ {
+ d_remote_headers = new Gee.HashMap<string, RefHeader>();
+ set_sort_func(sort_rows);
+ }
+
+ private int sort_rows(Gtk.ListBoxRow row1, Gtk.ListBoxRow row2)
+ {
+ var c1 = row1.get_child();
+ var c2 = row2.get_child();
+
+ var r1 = ((RefTyped)c1).ref_type;
+ var r2 = ((RefTyped)c2).ref_type;
+
+ // Compare types first
+ var rs1 = ref_type_sort_order(r1);
+ var rs2 = ref_type_sort_order(r2);
+
+ if (rs1 != rs2)
+ {
+ return rs1 < rs2 ? -1 : 1;
+ }
+
+ var head1 = c1 as RefHeader;
+ var ref1 = c1 as RefRow;
+
+ var head2 = c2 as RefHeader;
+ var ref2 = c2 as RefRow;
+
+ if ((head1 == null) != (head2 == null))
+ {
+ // Only one is a header
+ return head1 != null ? -1 : 1;
+ }
+ else if (head1 != null && head2 != null)
+ {
+ return head1.compare_to(head2);
+ }
+ else
+ {
+ return ref1.compare_to(ref2);
+ }
+ }
+
+ private void clear()
+ {
+ d_remote_headers = new Gee.HashMap<string, RefHeader>();
+
+ foreach (var child in get_children())
+ {
+ child.destroy();
+ }
+ }
+
+ private void add_header(Gitg.RefType ref_type, string name)
+ {
+ var header = new RefHeader(ref_type, name);
+ header.show();
+
+ add(header);
+ }
+
+ private RefHeader add_remote_header(string name)
+ {
+ var header = new RefHeader.remote(name);
+ header.show();
+
+ add(header);
+
+ return header;
+ }
+
+ private void add_ref(Gitg.Ref? reference)
+ {
+ var row = new RefRow(reference);
+ row.show();
+
+ add(row);
+ }
+
+ // Checks if the provided reference is a symbolic ref with the name HEAD.
+ private bool ref_is_a_symbolic_head(Gitg.Ref reference)
+ {
+ if (reference.get_reference_type() != Ggit.RefType.SYMBOLIC)
+ {
+ return false;
+ }
+
+ string name;
+
+ if (reference.parsed_name.rtype == Gitg.RefType.REMOTE)
+ {
+ name = reference.parsed_name.remote_branch;
+ }
+ else
+ {
+ name = reference.parsed_name.shortname;
+ }
+
+ return name == "HEAD";
+ }
+
+ public void refresh()
+ {
+ clear();
+
+ if (d_repository == null)
+ {
+ return;
+ }
+
+ add_ref(null);
+
+ add_header(Gitg.RefType.BRANCH, _("Branches"));
+ add_header(Gitg.RefType.REMOTE, _("Remotes"));
+ add_header(Gitg.RefType.TAG, _("Tags"));
+
+ try
+ {
+ d_repository.references_foreach_name((nm) => {
+ Gitg.Ref? r;
+
+ try
+ {
+ r = d_repository.lookup_reference(nm);
+ } catch { return 0; }
+
+ // Skip symbolic refs named HEAD since they aren't really
+ // useful to show (we get these for remotes for example)
+ if (ref_is_a_symbolic_head(r))
+ {
+ return 0;
+ }
+
+ if (r.parsed_name.rtype == Gitg.RefType.REMOTE)
+ {
+ var remote = r.parsed_name.remote_name;
+
+ if (!d_remote_headers.has_key(remote))
+ {
+ d_remote_headers[remote] = add_remote_header(remote);
+ }
+ }
+
+ add_ref(r);
+ return 0;
+ });
+ } catch {}
+ }
+
+ private RefRow? get_ref_row(Gtk.ListBoxRow row)
+ {
+ return row.get_child() as RefRow;
+ }
+
+ protected override void row_activated(Gtk.ListBoxRow row)
+ {
+ var r = get_ref_row(row);
+
+ if (r != null)
+ {
+ ref_activated(r.reference);
+ }
+ }
+}
+
+}
+
+// ex: ts=4 noet
diff --git a/gitg/history/gitg-history.vala b/gitg/history/gitg-history.vala
index 57bf6b8..3d29258 100644
--- a/gitg/history/gitg-history.vala
+++ b/gitg/history/gitg-history.vala
@@ -29,8 +29,8 @@ namespace GitgHistory
public GitgExt.Application? application { owned get; construct set; }
- private Navigation? d_navigation_model;
private Gitg.CommitModel? d_commit_list_model;
+
private Gee.HashSet<Ggit.OId> d_selected;
private ulong d_insertsig;
private Settings d_settings;
@@ -49,18 +49,6 @@ namespace GitgHistory
owned get { return "/org/gnome/gitg/Activities/History"; }
}
- [Notify]
- public Gitg.Repository repository
- {
- set
- {
- if (value != null)
- {
- reload();
- }
- }
- }
-
public void foreach_selected(GitgExt.ForeachCommitSelectionFunc func)
{
bool breakit = false;
@@ -88,25 +76,14 @@ namespace GitgHistory
d_selected = new Gee.HashSet<Ggit.OId>((Gee.HashDataFunc<Ggit.OId>)Ggit.OId.hash,
(Gee.EqualDataFunc<Ggit.OId>)Ggit.OId.equal);
- d_navigation_model = new Navigation(application.repository);
- d_navigation_model.ref_activated.connect((r) => {
- on_ref_activated(d_navigation_model, r);
- });
-
d_commit_list_model = new Gitg.CommitModel(application.repository);
d_commit_list_model.started.connect(on_commit_model_started);
d_commit_list_model.finished.connect(on_commit_model_finished);
update_sort_mode();
- application.bind_property("repository", d_navigation_model,
- "repository", BindingFlags.DEFAULT);
-
application.bind_property("repository", d_commit_list_model,
"repository", BindingFlags.DEFAULT);
-
- application.bind_property("repository", this,
- "repository", BindingFlags.DEFAULT);
}
private void update_sort_mode()
@@ -197,35 +174,10 @@ namespace GitgHistory
return -1;
}
- private void on_ref_activated(Navigation n, Gitg.Ref? r)
- {
- update_walker(n, r);
- }
-
- public void activate()
- {
- d_main.navigation_view.expand_all();
- d_main.navigation_view.select_first();
- }
-
- public void reload()
- {
- double vadj = d_main.navigation_view.get_vadjustment().get_value();
-
- d_navigation_model.reload();
- d_main.navigation_view.expand_all();
- d_main.navigation_view.select();
-
- d_main.navigation_view.size_allocate.connect((a) => {
- d_main.navigation_view.get_vadjustment().set_value(vadj);
- });
- }
-
private void build_ui()
{
d_main = new Paned();
- d_main.navigation_view.model = d_navigation_model;
d_main.commit_list_view.model = d_commit_list_model;
d_main.commit_list_view.get_selection().changed.connect((sel) => {
@@ -243,6 +195,15 @@ namespace GitgHistory
d_panels = new Gitg.UIElements<GitgExt.HistoryPanel>(extset,
d_main.stack_panel);
+
+ d_main.refs_list.ref_activated.connect((r) => {
+ update_walker(r);
+ });
+
+ application.bind_property("repository", d_main.refs_list,
+ "repository",
+ BindingFlags.DEFAULT |
+ BindingFlags.SYNC_CREATE);
}
private void update_walker(Navigation n, Gitg.Ref? head)
@@ -275,25 +236,45 @@ namespace GitgHistory
else
{
var included = new Ggit.OId[] {};
+ var repo = application.repository;
- // Simply push all the refs
- foreach (Gitg.Ref r in n.all)
+ try
{
- try
- {
- var resolved = r.resolve();
+ repo.references_foreach_name((nm) => {
+ Gitg.Ref? r;
try
{
- var t =
application.repository.lookup<Ggit.Tag>(resolved.get_target());
- included += t.get_target_id();
- }
- catch
+ r = repo.lookup_reference(nm);
+ } catch { return 0; }
+
+ try
{
- included += resolved.get_target();
- }
- } catch {}
- }
+ var resolved = r.resolve();
+
+ try
+ {
+ var t =
repo.lookup<Ggit.Tag>(resolved.get_target());
+ included += t.get_target_id();
+ }
+ catch
+ {
+ included += resolved.get_target();
+ }
+ } catch {}
+
+ return 0;
+ });
+ } catch {}
+
+ try
+ {
+ if (repo.is_head_detached())
+ {
+ var resolved = repo.get_head().resolve();
+ included += resolved.get_target();
+ }
+ } catch {}
d_commit_list_model.set_include(included);
}
diff --git a/gitg/resources/gitg-resources.xml b/gitg/resources/gitg-resources.xml
index 6c4b21c..f913934 100644
--- a/gitg/resources/gitg-resources.xml
+++ b/gitg/resources/gitg-resources.xml
@@ -11,6 +11,7 @@
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-clone-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-author-details-dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-history-paned.ui</file>
+ <file compressed="true" preprocess="xml-stripblanks">ui/gitg-history-ref-row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-commit-paned.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/gitg-commit-dialog.ui</file>
<file compressed="true">ui/style.css</file>
diff --git a/gitg/resources/ui/gitg-history-paned.ui b/gitg/resources/ui/gitg-history-paned.ui
index 537b1a6..d9d5ebb 100644
--- a/gitg/resources/ui/gitg-history-paned.ui
+++ b/gitg/resources/ui/gitg-history-paned.ui
@@ -32,11 +32,9 @@
<class name="sidebar"/>
</style>
<child>
- <object class="GitgHistoryNavigationView" id="d_navigation_view">
+ <object class="GitgHistoryRefsList" id="d_refs_list">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="headers_visible">False</property>
- <property name="name">tree_view_navigation</property>
</object>
</child>
</object>
diff --git a/gitg/resources/ui/gitg-history-ref-row.ui b/gitg/resources/ui/gitg-history-ref-row.ui
new file mode 100644
index 0000000..46b0e2f
--- /dev/null
+++ b/gitg/resources/ui/gitg-history-ref-row.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.3 -->
+ <!-- interface-requires gitg 0.0 -->
+ <!-- interface-requires gd 1.0 -->
+ <template class="GitgHistoryRefRow">
+ <property name="orientation">horizontal</property>
+ <property name="visible">True</property>
+ <property name="margin_left">16</property>
+ <child>
+ <object class="GtkImage" id="d_icon">
+ <property name="icon_size">1</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="d_label">
+ <property name="visible">True</property>
+ <property name="ellipsize">middle</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ </template>
+</interface>
+<!-- vi:ts=2:et -->
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]