[gnome-contacts/new-design] Add new Column.Row container to nicer handle the content pane
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-contacts/new-design] Add new Column.Row container to nicer handle the content pane
- Date: Mon, 12 Dec 2011 15:32:58 +0000 (UTC)
commit 172267f0e49f0afbce5731973b9178bb054c42ec
Author: Alexander Larsson <alexl redhat com>
Date: Fri Dec 9 14:44:18 2011 +0100
Add new Column.Row container to nicer handle the content pane
This will also be useful later for the list.
src/contacts-contact-pane.vala | 135 +++++++++--
src/contacts-row.vala | 489 ++++++++++++++++++++++++++++++++++++----
2 files changed, 551 insertions(+), 73 deletions(-)
---
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 0369253..0b15299 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -413,11 +413,93 @@ public class Contacts.AvatarMenu : Menu {
}
}
+public class Contacts.FieldRow : Contacts.Row {
+ int start;
+
+ public FieldRow(ContactPane pane) {
+ base (3);
+
+ start = 0;
+
+ set_column_min_width (0, 32);
+ set_column_min_width (1, 400);
+ set_column_max_width (1, 450);
+ set_column_min_width (2, 32);
+ set_column_spacing (0, 8);
+ set_column_spacing (1, 8);
+ }
+
+ public void pack (Widget w) {
+ this.attach (w, 1, start++);
+ }
+
+ public void label (string s) {
+ var l = new Label (s);
+ l.set_halign (Align.START);
+ l.get_style_context ().add_class ("dim-label");
+ pack (l);
+ }
+
+ public void header (string s) {
+ var l = new Label (s);
+ l.set_markup (
+ "<span font='24px'>%s</span>".printf (s));
+ l.set_halign (Align.START);
+ pack (l);
+ }
+
+ public void text (string s, bool wrap = false) {
+ var l = new Label (s);
+ if (wrap) {
+ l.set_line_wrap (true);
+ l.set_line_wrap_mode (Pango.WrapMode.WORD_CHAR);
+ } else {
+ l.set_ellipsize (Pango.EllipsizeMode.END);
+ }
+ l.set_halign (Align.START);
+ pack (l);
+ }
+
+ public void text_detail (string text, string detail, bool wrap = false) {
+ var grid = new Grid ();
+
+ var l = new Label (text);
+ l.set_hexpand (true);
+ l.set_halign (Align.START);
+ if (wrap) {
+ l.set_line_wrap (true);
+ l.set_line_wrap_mode (Pango.WrapMode.WORD_CHAR);
+ } else {
+ l.set_ellipsize (Pango.EllipsizeMode.END);
+ }
+ grid.add (l);
+
+ l = new Label (detail);
+ l.set_halign (Align.END);
+ l.get_style_context ().add_class ("dim-label");
+
+ grid.set_halign (Align.FILL);
+ grid.add (l);
+
+ pack (grid);
+ }
+
+ public void left_add (Widget widget) {
+ this.attach (widget, 0, 0);
+ widget.set_halign (Align.END);
+ }
+
+ public void right_add (Widget widget) {
+ this.attach (widget, 2, 0);
+ widget.set_halign (Align.START);
+ }
+}
+
public class Contacts.PersonaSheet : Grid {
ContactPane pane;
Persona persona;
- Row header;
- Row footer;
+ FieldRow header;
+ FieldRow footer;
abstract class Field : Grid {
public class string label_name;
@@ -425,14 +507,14 @@ public class Contacts.PersonaSheet : Grid {
public PersonaSheet sheet { get; construct; }
public int row_nr { get; construct; }
public bool added;
- Row label_row;
+ FieldRow label_row;
public abstract void populate ();
construct {
this.set_orientation (Orientation.VERTICAL);
- label_row = new Row (sheet.pane);
+ label_row = new FieldRow (sheet.pane);
this.add (label_row);
label_row.label (label_name);
}
@@ -453,8 +535,8 @@ public class Contacts.PersonaSheet : Grid {
}
}
- public Row new_row () {
- var row = new Row (sheet.pane);
+ public FieldRow new_row () {
+ var row = new FieldRow (sheet.pane);
this.add (row);
return row;
}
@@ -480,7 +562,7 @@ public class Contacts.PersonaSheet : Grid {
var button = new Button();
button.set_relief (ReliefStyle.NONE);
button.add (image);
- row.right.add (button);
+ row.right_add (button);
}
}
}
@@ -496,8 +578,7 @@ public class Contacts.PersonaSheet : Grid {
var emails = Contact.sort_fields<EmailFieldDetails>(details.email_addresses);
foreach (var email in emails) {
var row = new_row ();
- row.text (email.value);
- row.detail (TypeSet.general.format_type (email));
+ row.text_detail (email.value, TypeSet.general.format_type (email));
}
}
}
@@ -513,8 +594,7 @@ public class Contacts.PersonaSheet : Grid {
var phone_numbers = Contact.sort_fields<PhoneFieldDetails>(details.phone_numbers);
foreach (var phone in phone_numbers) {
var row = new_row ();
- row.text (phone.value);
- row.detail (TypeSet.phone.format_type (phone));
+ row.text_detail (phone.value, TypeSet.phone.format_type (phone));
}
}
}
@@ -560,7 +640,7 @@ public class Contacts.PersonaSheet : Grid {
var button = new Button();
button.set_relief (ReliefStyle.NONE);
button.add (image);
- row.right.add (button);
+ row.right_add (button);
}
}
}
@@ -608,10 +688,13 @@ public class Contacts.PersonaSheet : Grid {
foreach (var addr in details.postal_addresses) {
var row = new_row ();
- row.detail (TypeSet.general.format_type (addr));
string[] strs = Contact.format_address (addr.value);
+ int i = 0;
foreach (var s in strs) {
- row.text (s, true);
+ if (i++ == 0)
+ row.text_detail (s, TypeSet.general.format_type (addr), true);
+ else
+ row.text (s, true);
}
}
}
@@ -650,20 +733,17 @@ public class Contacts.PersonaSheet : Grid {
Contact.persona_has_writable_property (persona, "postal-addresses");
if (!persona.store.is_primary_store) {
- header = new Row (pane);
+ header = new FieldRow (pane);
this.attach (header, 0, row_nr++, 1, 1);
- var label = new Label ("");
- label.set_markup (
- "<span font='24px'>%s</span>".printf (Contact.format_persona_store_name (persona.store)));
- header.pack_start (label);
+ header.header (Contact.format_persona_store_name (persona.store));
if (!editable) {
var image = new Image.from_icon_name ("changes-prevent-symbolic", IconSize.MENU);
image.get_style_context ().add_class ("dim-label");
- header.left.add (image);
- header.left.set (1.0f, 0.5f, 0, 1.0f);
+ image.set_valign (Align.CENTER);
+ header.left_add (image);
}
}
@@ -676,10 +756,13 @@ public class Contacts.PersonaSheet : Grid {
}
if (editable) {
- footer = new Row (pane);
+ footer = new FieldRow (pane);
this.attach (footer, 0, row_nr++, 1, 1);
+
var b = new Button.with_label ("Add detail...");
- footer.pack_start (b);
+ b.set_halign (Align.START);
+
+ footer.pack (b);
}
}
@@ -847,11 +930,11 @@ public class Contacts.ContactPane : ScrolledWindow {
this.get_child().get_style_context ().add_class ("contact-pane");
- var top_row = new Row (this);
- top_row.left.set_size_request (32, -1);
+ var top_row = new FieldRow (this);
top_grid.add (top_row);
card_grid = new Grid ();
- top_row.pack_start (card_grid, Align.FILL);
+ card_grid.set_vexpand (false);
+ top_row.pack (card_grid);
personas_grid = new Grid ();
personas_grid.set_orientation (Orientation.VERTICAL);
diff --git a/src/contacts-row.vala b/src/contacts-row.vala
index df16b15..3361105 100644
--- a/src/contacts-row.vala
+++ b/src/contacts-row.vala
@@ -18,69 +18,464 @@
using Gtk;
-public class Contacts.Row : Grid {
- public Alignment left;
- public Grid content;
- public Alignment right;
- int start;
+public class Contacts.Row : Container {
+ struct RowInfo {
+ int min_height;
+ int nat_height;
+ bool expand;
+ }
- public Row (ContactPane pane) {
- this.set_orientation (Orientation.HORIZONTAL);
- this.set_column_spacing (8);
+ struct ColumnInfo {
+ int min_width;
+ int nat_width;
+ int max_width;
+ int prio;
+ int spacing;
+ }
+ struct Child {
+ Widget? widget;
+ }
- this.set_hexpand (true);
- this.set_vexpand (false);
+ int n_columns;
+ int n_rows;
+ ColumnInfo[] column_info;
+ Child[,] row_children;
- left = new Alignment (1,0,0,0);
- left.set_hexpand (true);
- pane.border_size_group.add_widget (left);
+ int[] cached_widths;
+ int cached_widths_for_width;
- content = new Grid ();
- content.set_size_request (450, -1);
+ public Row (int n_columns) {
+ int i;
- right = new Alignment (0,0,0,0);
- right.set_hexpand (true);
- pane.border_size_group.add_widget (right);
+ set_has_window (false);
+ set_redraw_on_allocate (false);
+ set_hexpand (true);
- this.attach (left, 0, 0, 1, 1);
- this.attach (content, 1, 0, 1, 1);
- this.attach (right, 2, 0, 1, 1);
- this.show_all ();
+ this.n_columns = n_columns;
+ n_rows = 1;
+ row_children = new Child[n_columns, n_rows];
+ column_info = new ColumnInfo[n_columns];
+ for (i = 0; i < n_columns; i++) {
+ column_info[i].min_width = 0;
+ column_info[i].nat_width = -1;
+ column_info[i].max_width = -1;
+ column_info[i].prio = 0;
+ }
}
- public void pack_start (Widget w, Align align = Align.START) {
- content.attach (w, 0, start++, 1, 1);
- w.set_hexpand (true);
- w.set_halign (align);
+ public void set_column_priority (int column, int priority) {
+ if (column >= n_columns)
+ return;
+
+ column_info[column].prio = priority;
+
+ cached_widths = null;
+ if (this.get_visible ())
+ this.queue_resize ();
}
- public void pack_end (Widget w) {
- content.attach (w, 1, 0, 1, 1);
- w.set_hexpand (false);
- w.set_halign (Align.END);
+ public void set_column_min_width (int column, int min_width) {
+ if (column >= n_columns)
+ return;
+
+ column_info[column].min_width = min_width;
+ cached_widths = null;
+ if (this.get_visible ())
+ this.queue_resize ();
}
- public void label (string s) {
- var l = new Label (s);
- l.get_style_context ().add_class ("dim-label");
- pack_start (l);
+ public void set_column_max_width (int column, int max_width) {
+ if (column >= n_columns)
+ return;
+
+ column_info[column].max_width = max_width;
+ cached_widths = null;
+ if (this.get_visible ())
+ this.queue_resize ();
+ }
+
+ public void set_column_spacing (int column, int spacing) {
+ if (column >= n_columns)
+ return;
+
+ column_info[column].spacing = spacing;
+ cached_widths = null;
+ if (this.get_visible ())
+ this.queue_resize ();
}
- public void text (string s, bool wrap = false) {
- var l = new Label (s);
- if (wrap) {
- l.set_line_wrap (true);
- l.set_line_wrap_mode (Pango.WrapMode.WORD_CHAR);
- } else {
- l.set_ellipsize (Pango.EllipsizeMode.END);
+ public void attach (Widget widget, int attach_col, int attach_row) {
+ if (attach_col >= n_columns || attach_row >= n_rows) {
+ int old_n_columns = n_columns;
+ int old_n_rows = n_rows;
+
+ if (attach_col >= n_columns) {
+ n_columns = attach_col + 1;
+
+ var old_column_info = (owned)column_info;
+ column_info = new ColumnInfo[n_columns];
+
+ for (int i = 0; i < n_columns; i++) {
+ if (i < old_n_columns)
+ column_info[i] = old_column_info[i];
+ else {
+ column_info[i].min_width = 0;
+ column_info[i].nat_width = -1;
+ column_info[i].max_width = -1;
+ column_info[i].prio = 0;
+ }
+ }
+ }
+
+ if (attach_row >= n_rows)
+ n_rows = attach_row + 1;
+
+ var old_row_children = (owned)row_children;
+ row_children = new Child[n_columns, n_rows];
+
+ for (int row = 0; row < n_rows; row++) {
+ for (int col = 0; col < n_columns; col++) {
+ if (row < old_n_rows &&
+ col < old_n_columns) {
+ row_children[col, row] = (owned)old_row_children[col, row];
+ }
+ }
+ }
}
- pack_start (l);
+
+ Child *child_info = &row_children[attach_col, attach_row];
+ if (child_info.widget != null) {
+ remove (child_info.widget);
+ }
+
+ child_info.widget = widget;
+ widget.set_parent (this);
}
- public void detail (string s) {
- var l = new Label (s);
- l.get_style_context ().add_class ("dim-label");
- pack_end (l);
+ public override void add (Widget widget) {
+ for (int row = 0; row < n_rows; row++) {
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget == null) {
+ attach (widget, col, row);
+ return;
+ }
+ }
+ }
+ attach (widget, 0, n_rows);
}
-}
+ public override void remove (Widget widget) {
+ for (int row = 0; row < n_rows; row++) {
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget == widget) {
+ bool was_visible = widget.get_visible ();
+
+ widget.unparent ();
+
+ child_info.widget = null;
+
+ if (was_visible && this.get_visible ())
+ this.queue_resize ();
+
+ return;
+ }
+ }
+ }
+ }
+
+ public override void forall_internal (bool include_internals,
+ Gtk.Callback callback) {
+ for (int row = 0; row < n_rows; row++) {
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget != null) {
+ callback (child_info.widget);
+ }
+ }
+ }
+ }
+
+ public override void compute_expand_internal (out bool hexpand, out bool vexpand) {
+ hexpand = false;
+ for (int i = 0; i < n_columns; i++) {
+ var info = &column_info[i];
+ if (info.max_width == -1 &&
+ info.max_width != info.min_width) {
+ hexpand = true;
+ break;
+ }
+ }
+
+ vexpand = false;
+ for (int row = 0; row < n_rows; row++) {
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget != null) {
+ vexpand |= child_info.widget.compute_expand (Orientation.VERTICAL);
+ }
+ }
+ }
+ }
+
+ public override Type child_type () {
+ return typeof (Widget);
+ }
+
+ public override Gtk.SizeRequestMode get_request_mode () {
+ return SizeRequestMode.HEIGHT_FOR_WIDTH;
+ }
+
+ public override void get_preferred_height (out int minimum_height, out int natural_height) {
+ int natural_width;
+ get_preferred_width (null, out natural_width);
+ get_preferred_height_for_width (natural_width, out minimum_height, out natural_height);
+ }
+
+ int[] distribute_widths (int width) {
+ if (cached_widths != null &&
+ cached_widths_for_width == width)
+ return cached_widths;
+
+ int max_prio = 0;
+ var widths = new int[n_columns];
+
+ /* First distribute the min widths */
+ for (int i = 0; i < n_columns; i++) {
+ ColumnInfo *info = &column_info[i];
+
+ if (info->prio > max_prio)
+ max_prio = info->prio;
+
+ if (width > info.min_width) {
+ widths[i] = info.min_width;
+ width -= info.min_width;
+ } else if (width > 0) {
+ widths[i] = width;
+ width = 0;
+ } else {
+ widths[i] = 0;
+ }
+ width -= info.spacing;
+ }
+
+ /* Distribute remaining width equally among
+ children with same priority, up to max */
+ for (int prio = max_prio; width > 0 && prio >= 0; prio--) {
+ int n_children;
+
+ while (width > 0) {
+ n_children = 0;
+ int max_extra = width;
+
+ for (int i = 0; i < n_columns; i++) {
+ ColumnInfo *info = &column_info[i];
+
+ if (info.prio == prio &&
+ (info.max_width < 0 ||
+ widths[i] < info.max_width)) {
+ n_children++;
+
+ if (info.max_width >= 0 &&
+ info.max_width - widths[i] < max_extra)
+ max_extra = info.max_width - widths[i];
+ }
+ }
+
+ if (n_children == 0)
+ break; // No more unsatisfied children on this prio
+
+ int distribute = int.min (width, max_extra * n_children);
+ width -= distribute;
+
+ int per_child = distribute / n_children;
+ int per_child_extra = distribute % n_children;
+ int per_child_extra_sum = 0;
+
+ for (int i = 0; i < n_columns; i++) {
+ ColumnInfo *info = &column_info[i];
+
+ if (info.prio == prio &&
+ (info.max_width < 0 ||
+ widths[i] < info.max_width)) {
+ widths[i] += per_child;
+ per_child_extra_sum += per_child_extra;
+ if (per_child_extra_sum > distribute) {
+ widths[i] += 1;
+ per_child_extra_sum -= distribute;
+ }
+ }
+ }
+ }
+ }
+
+ cached_widths = widths;
+ cached_widths_for_width = width;
+ return widths;
+ }
+
+ int[] distribute_heights (int height, RowInfo[] row_info) {
+ var heights = new int[n_rows];
+
+ /* First distribute the min heights */
+ for (int i = 0; i < n_rows; i++) {
+ RowInfo *info = &row_info[i];
+
+ if (height > info.min_height) {
+ heights[i] = info.min_height;
+ height -= info.min_height;
+ } else if (height > 0) {
+ heights[i] = height;
+ height = 0;
+ } else {
+ heights[i] = 0;
+ }
+ }
+
+ /* Distribute remaining height equally among
+ children that have not filled up their natural size */
+ int n_children;
+
+ while (height > 0) {
+ n_children = 0;
+ int max_extra = height;
+
+ for (int i = 0; i < n_rows; i++) {
+ RowInfo *info = &row_info[i];
+
+ if (info.expand || heights[i] < info.nat_height) {
+ n_children++;
+
+ if (info.nat_height < heights[i] &&
+ info.nat_height - heights[i] < max_extra)
+ max_extra = info.nat_height - heights[i];
+ }
+ }
+
+ if (n_children == 0)
+ break;
+
+ int distribute = int.min (height, max_extra * n_children);
+ height -= distribute;
+
+ int per_child = distribute / n_children;
+ int per_child_extra = distribute % n_children;
+ int per_child_extra_sum = 0;
+
+ for (int i = 0; i < n_rows; i++) {
+ RowInfo *info = &row_info[i];
+
+ if (heights[i] < info.nat_height ||
+ n_children == n_rows) {
+ heights[i] += per_child;
+ per_child_extra_sum += per_child_extra;
+ if (per_child_extra_sum > distribute) {
+ heights[i] += 1;
+ per_child_extra_sum -= distribute;
+ }
+ }
+ }
+ }
+
+ return heights;
+ }
+
+ private RowInfo[] get_row_heights_for_widths (int[] widths) {
+ RowInfo[] info = new RowInfo[n_rows];
+
+ for (int row = 0; row < n_rows; row++) {
+ int row_min = 0, row_nat = 0;
+ bool first = true;
+ bool expand = false;
+
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget != null) {
+ int child_min, child_nat;
+
+ child_info.widget.get_preferred_height_for_width (widths[col], out child_min, out child_nat);
+
+ expand |= child_info.widget.compute_expand (Orientation.VERTICAL);
+
+ if (first) {
+ first = false;
+ row_min = child_min;
+ row_nat = child_nat;
+ } else {
+ row_min = int.max (row_min, child_min);
+ row_nat = int.max (row_nat, child_nat);
+ }
+ }
+ }
+ info[row].min_height = row_min;
+ info[row].nat_height = row_nat;
+ info[row].expand = expand;
+ }
+ return info;
+ }
+
+ public override void get_preferred_height_for_width (int width, out int minimum_height, out int natural_height) {
+ var widths = distribute_widths (width);
+ var row_info = get_row_heights_for_widths (widths);
+
+ minimum_height = 0;
+ natural_height = 0;
+
+ for (int row = 0; row < n_rows; row++) {
+ minimum_height += row_info[row].min_height;
+ natural_height += row_info[row].nat_height;
+ }
+ }
+
+ public override void get_preferred_width (out int minimum_width, out int natural_width) {
+ minimum_width = 0;
+ natural_width = 0;
+ for (int i = 0; i < n_columns; i++) {
+ minimum_width += column_info[i].min_width;
+ if (column_info[i].nat_width >= 0)
+ natural_width += column_info[i].nat_width;
+ else if (column_info[i].max_width >= 0)
+ natural_width += column_info[i].max_width;
+ else
+ natural_width += column_info[i].min_width;
+ minimum_width += column_info[i].spacing;
+ natural_width += column_info[i].spacing;
+ }
+ }
+
+ public override void get_preferred_width_for_height (int height, out int minimum_width, out int natural_width) {
+ get_preferred_width (out minimum_width, out natural_width);
+ }
+
+ public override void size_allocate (Gtk.Allocation allocation) {
+ var widths = distribute_widths (allocation.width);
+ var row_info = get_row_heights_for_widths (widths);
+ var heights = distribute_heights (allocation.height, row_info);
+
+ set_allocation (allocation);
+
+ int y = 0;
+ for (int row = 0; row < n_rows; row ++) {
+ int x = 0;
+ for (int col = 0; col < n_columns; col++) {
+ Child *child_info = &row_children[col, row];
+ if (child_info.widget != null) {
+ Allocation child_allocation = { 0, 0, 0, 0};
+
+ child_allocation.width = widths[col]; // calculate_child_width (child_info.widget, widths[col]);
+ child_allocation.height = heights[row];
+
+ if (get_direction () == TextDirection.RTL)
+ child_allocation.x = allocation.x + allocation.width - x - child_allocation.width;
+ else
+ child_allocation.x = allocation.x + x;
+ child_allocation.y = allocation.y + y;
+ child_info.widget.size_allocate (child_allocation);
+ }
+ x += widths[col] + column_info[col].spacing;
+ }
+ y += heights[row];
+ }
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]