[glom] Details: Align widgets in the first columns of neighbouring layout groups.



commit ec80669c75bef3edf13783ca7c22a018758fa053
Author: Murray Cumming <murrayc murrayc com>
Date:   Sat Mar 6 21:39:49 2010 +0100

    Details: Align widgets in the first columns of neighbouring layout groups.
    
    * glom/utility_widgets/flowtable.[h|cc]: Added get_column_for_first_widget(),
    to discover what column a widget is in, and whether it has even been assigned
    to a column yet (if the size has been allocated already.)
    FlowTableItem: Remember whether the item has a column number and what it is.
    on_size_allocate(): Remember the column numbers of items for later retrieval.
    * glom/mode_data/flowtablewithfields.[h|cc]: Info, add_field_at_position():
    Store the pointer to eventbox too, so we can later discover its column from
    the base FlowTable.
    Add align_child_group_labels(), to make all first-level labels in child
    flowtables share a single Gtk::SizeGroup, making them align.
    Add apply_size_group_to_labels() to actually add the labels to the size group.
    add_layout_group_at_position(): Call align_child_group_labels() on new
    sub-flowtables.
    on_size_allocate(): After the base class has allocated the spaces for the
    child widgets, after it therefore knows the column positions for the widgets,
    call apply_size_group_to_labels() to make them align.
    * glom/mode_data/box_data_details.cc: create_layout(): Call
    align_child_group_labels() to align items in top-level groups.

 ChangeLog                             |   23 ++++++++
 glom/mode_data/box_data_details.cc    |    2 +
 glom/mode_data/flowtablewithfields.cc |   91 +++++++++++++++++++++++++++++++++
 glom/mode_data/flowtablewithfields.h  |   20 +++++++-
 glom/utility_widgets/flowtable.cc     |   47 +++++++++++++++--
 glom/utility_widgets/flowtable.h      |   23 +++++++-
 6 files changed, 197 insertions(+), 9 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 77bd3f8..546101f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,28 @@
 2010-03-06  Murray Cumming  <murrayc murrayc com>
 
+	Details: Align widgets in the first columns of neighbouring layout groups.
+
+	* glom/utility_widgets/flowtable.[h|cc]: Added get_column_for_first_widget(), 
+	to discover what column a widget is in, and whether it has even been assigned 
+	to a column yet (if the size has been allocated already.)
+	FlowTableItem: Remember whether the item has a column number and what it is.
+	on_size_allocate(): Remember the column numbers of items for later retrieval.
+	* glom/mode_data/flowtablewithfields.[h|cc]: Info, add_field_at_position():
+	Store the pointer to eventbox too, so we can later discover its column from 
+	the base FlowTable.
+	Add align_child_group_labels(), to make all first-level labels in child 
+	flowtables share a single Gtk::SizeGroup, making them align.
+	Add apply_size_group_to_labels() to actually add the labels to the size group.
+	add_layout_group_at_position(): Call align_child_group_labels() on new 
+	sub-flowtables.
+	on_size_allocate(): After the base class has allocated the spaces for the 
+	child widgets, after it therefore knows the column positions for the widgets, 
+	call apply_size_group_to_labels() to make them align.
+	* glom/mode_data/box_data_details.cc: create_layout(): Call 
+	align_child_group_labels() to align items in top-level groups.
+
+2010-03-06  Murray Cumming  <murrayc murrayc com>
+
   Details: Do not make field widgets too wide, so this fits on a laptop screen.
   
 	* glom/utils_ui.cc: get_suitable_field_width_for_widget(): Do not add 150 
diff --git a/glom/mode_data/box_data_details.cc b/glom/mode_data/box_data_details.cc
index 2a60f2d..43061c5 100644
--- a/glom/mode_data/box_data_details.cc
+++ b/glom/mode_data/box_data_details.cc
@@ -231,6 +231,8 @@ void Box_Data_Details::create_layout()
     {
       m_FlowTable.add_layout_group(*iter);
     }
+    
+    m_FlowTable.align_child_group_labels();
   }
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
diff --git a/glom/mode_data/flowtablewithfields.cc b/glom/mode_data/flowtablewithfields.cc
index 5d38504..5572b53 100644
--- a/glom/mode_data/flowtablewithfields.cc
+++ b/glom/mode_data/flowtablewithfields.cc
@@ -44,6 +44,7 @@ namespace Glom
   
 FlowTableWithFields::Info::Info()
 : m_first(0),
+  m_first_eventbox(0),
   m_second(0),
   m_checkbutton(0)
 {
@@ -248,6 +249,8 @@ void FlowTableWithFields::add_layout_group_at_position(const sharedptr<LayoutGro
     flow_table->signal_related_record_changed().connect( sigc::mem_fun(*this, &FlowTableWithFields::on_flowtable_related_record_changed) );
     flow_table->signal_requested_related_details().connect( sigc::mem_fun(*this, &FlowTableWithFields::on_flowtable_requested_related_details) );
     flow_table->signal_script_button_clicked().connect( sigc::mem_fun(*this, &FlowTableWithFields::on_script_button_clicked) );
+    
+    flow_table->align_child_group_labels();
   }
 }
 
@@ -556,6 +559,7 @@ void FlowTableWithFields::add_field_at_position(const sharedptr<LayoutItem_Field
   
   Gtk::EventBox* eventbox = Gtk::manage(new Gtk::EventBox());
   eventbox->add(*info.m_first);
+  info.m_first_eventbox = eventbox; //Remember it so we can retrieve the column number later from FlowTable.
   eventbox->set_visible_window(false);
   eventbox->set_events(Gdk::ALL_EVENTS_MASK);
   eventbox->show_all();
@@ -1242,6 +1246,93 @@ void FlowTableWithFields::on_flowtable_requested_related_details(const Glib::ust
   signal_requested_related_details().emit(table_name, primary_key_value);
 }
 
+void FlowTableWithFields::apply_size_group_to_labels(const Glib::RefPtr<Gtk::SizeGroup>& size_group)
+{
+  //Remove widgets from any existing size group:
+  for(type_listFields::iterator iter = m_listFields.begin(); iter != m_listFields.end(); ++iter)
+  {
+    Info info = *iter;
+    Gtk::Label* label = info.m_first;
+    Glib::RefPtr<Gtk::SizeGroup> previous_size_group = info.m_first_in_sizegroup;
+    if(!label || !previous_size_group)
+      continue;
+      
+    if(previous_size_group != size_group)
+    {
+      previous_size_group->remove_widget(*label);
+      info.m_first_in_sizegroup.clear();
+    }
+  }
+  
+  m_size_group = size_group;
+  
+  if(!size_group)
+    return;
+     
+  for(type_listFields::iterator iter = m_listFields.begin(); iter != m_listFields.end(); ++iter)
+  {
+    Info info = *iter;
+    Gtk::Label* label = info.m_first;
+    if(!label)
+      continue;
+      
+    Gtk::EventBox* label_parent = info.m_first_eventbox;
+    if(!label_parent)
+      continue;
+            
+    //Only align labels in the first column, because items in separate columns 
+    //couldn't be aligned vertically anyway, and this would cause extra space.
+    //TODO: Use a different SizeGroup for items in 2nd columns?
+    guint column = 0;
+    const bool column_allocated = get_column_for_first_widget(*label_parent, column);
+    if(!column_allocated || (column > 0))
+      continue;
+    
+    if(info.m_first_in_sizegroup != size_group)
+    {
+      size_group->add_widget(*label);
+      info.m_first_in_sizegroup = size_group; //Remember it so we can remove it later.
+    }
+  } 
+}
+
+void FlowTableWithFields::align_child_group_labels()
+{
+  //Don't bother if there are not >1 groups to align:
+  if(m_sub_flow_tables.size() < 2)
+    return;
+    
+  //Create a size group and tell all groups to use is for their labels:
+  Glib::RefPtr<Gtk::SizeGroup> size_group = 
+    Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
+  for(type_sub_flow_tables::iterator iter = m_sub_flow_tables.begin(); iter != m_sub_flow_tables.end(); ++iter)
+  {
+    FlowTableWithFields* subtable = *iter;
+    if(subtable)
+      subtable->apply_size_group_to_labels(size_group);
+  }
+}
+
+void FlowTableWithFields::on_size_allocate(Gtk::Allocation& allocation)
+{
+  FlowTable::on_size_allocate(allocation);
+  
+  sharedptr<const LayoutGroup> group = get_layout_group();
+  Glib::ustring group_name;
+  if(group)
+     group_name = group->get_name();
+
+  //The widgets have now been allocated their sizes and columns, 
+  //so this should be able to work:
+  if(m_columns_allocated_changed)
+  {
+    apply_size_group_to_labels(m_size_group);
+    
+    //Prevent unnecessary repeated (endless?) size allocation requested:
+    m_columns_allocated_changed = false;
+  }
+}
+  
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 
 void FlowTableWithFields::on_dnd_add_layout_item_by_type(int item_type_num, Gtk::Widget* above_widget)
diff --git a/glom/mode_data/flowtablewithfields.h b/glom/mode_data/flowtablewithfields.h
index 1b71a1e..e54424d 100644
--- a/glom/mode_data/flowtablewithfields.h
+++ b/glom/mode_data/flowtablewithfields.h
@@ -102,6 +102,17 @@ public:
   virtual void set_design_mode(bool value = true);
 
   virtual void remove_all();
+  
+  /** Apply the size group to all field labels.
+   * By calling this method on multiple FlowTables, the field widgets in 
+   * different groups can then align.
+   */
+  void apply_size_group_to_labels(const Glib::RefPtr<Gtk::SizeGroup>& size_group);
+  
+  /** Create a size group and make all the labels in child flowtables use it,
+   * making them align.
+   */
+  void align_child_group_labels();
 
   /** Get the layout structure, which might have changed in the child widgets since 
    * the whole widget structure was built.
@@ -180,9 +191,11 @@ private:
     Info();
 
     sharedptr<const LayoutItem_Field> m_field; //Store the field information so we know the title, ID, and type.
-    Glib::ustring m_group;
 
     Gtk::Label* m_first;
+    Gtk::EventBox* m_first_eventbox; //The label is often inside an eventbox.
+    Glib::RefPtr<Gtk::SizeGroup> m_first_in_sizegroup; //Just to avoid a warning when removing a widget not in a group.
+
     DataWidget* m_second;
     Gtk::CheckButton* m_checkbutton; //Used instead of first and second if it's a bool.
   };
@@ -212,6 +225,8 @@ private:
   void add_layout_group_at_position(const sharedptr<LayoutGroup>& group, const type_list_layoutwidgets::iterator& add_before);
   void add_layout_notebook_at_position(const sharedptr<LayoutItem_Notebook>& notebook, const type_list_layoutwidgets::iterator& add_before);
   void add_layout_portal_at_position(const sharedptr<LayoutItem_Portal>& portal, const type_list_layoutwidgets::iterator& add_before);
+  
+  virtual void on_size_allocate(Gtk::Allocation& allocation);
 
 #ifndef GLOM_ENABLE_CLIENT_ONLY
 
@@ -244,6 +259,9 @@ private:
   Gtk::Alignment* m_placeholder;
   
   Glib::ustring m_table_name;
+  
+  //Size group shared by this widget's sibling FlowTables.
+  Glib::RefPtr<Gtk::SizeGroup> m_size_group;
 
   type_signal_field_edited m_signal_field_edited;
   type_signal_field_open_details_requested m_signal_field_open_details_requested;
diff --git a/glom/utility_widgets/flowtable.cc b/glom/utility_widgets/flowtable.cc
index 02faee4..2e08514 100644
--- a/glom/utility_widgets/flowtable.cc
+++ b/glom/utility_widgets/flowtable.cc
@@ -177,7 +177,9 @@ FlowTable::FlowTableItem::FlowTableItem(Gtk::Widget* first, FlowTable* /* flowta
 : m_first(first),
   m_second(0),
   m_expand_first_full(false),
-  m_expand_second(false)
+  m_expand_second(false),
+  m_has_allocated_column(false),
+  m_allocated_column(0)
 {
 
 }
@@ -186,7 +188,9 @@ FlowTable::FlowTableItem::FlowTableItem(Gtk::Widget* first, Gtk::Widget* second,
 : m_first(first),
   m_second(second),
   m_expand_first_full(false),
-  m_expand_second(false)
+  m_expand_second(false),
+  m_has_allocated_column(false),
+  m_allocated_column(0)
 {
 
 }
@@ -201,6 +205,7 @@ FlowTable::FlowTable()
   // rather annoying, though I don't see another possibility at the moment. armin.
   Glib::ObjectBase("Glom_FlowTable"),
 #endif // ! !defined(GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED)
+  m_columns_allocated_changed(false),
   m_columns_count(1),
   m_column_padding(Utils::DEFAULT_SPACING_SMALL), //A sane default.
   m_row_padding(Utils::DEFAULT_SPACING_SMALL), //A sane default.
@@ -722,6 +727,7 @@ void FlowTable::on_size_allocate(Gtk::Allocation& allocation)
 
   int column_child_y_start = allocation.get_y();
 
+  guint column_number = 0; //Just for remembering it for later.
   guint count = m_children.size();
   for(guint i = 0; i < count; ++i)
   {
@@ -735,7 +741,8 @@ void FlowTable::on_size_allocate(Gtk::Allocation& allocation)
     {
       //start a new column:
       column_child_y_start = allocation.get_y();
-      int column_x_start_plus_singles = column_x_start + singles_max_width;
+      ++column_number;
+      const int column_x_start_plus_singles = column_x_start + singles_max_width;
       column_x_start = column_x_start_second + second_max_width;
       column_x_start = MAX(column_x_start, column_x_start_plus_singles); //Maybe the single items take up even more width.
       column_x_start += m_column_padding;
@@ -786,7 +793,7 @@ void FlowTable::on_size_allocate(Gtk::Allocation& allocation)
         something_added = true;
       }
 
-      if(child_is_visible(second))
+      if(true) //Unecessary check: child_is_visible(second))
       {
         //Assign space to the child:
 
@@ -820,6 +827,15 @@ void FlowTable::on_size_allocate(Gtk::Allocation& allocation)
 
     if(something_added)
     {
+      //Let later code know where the widgets are, and whether the layout has 
+      //changed since m_columns_allocated_changed was last set to false.
+      if(!item.m_has_allocated_column || (item.m_allocated_column != column_number))
+      {
+        item.m_allocated_column = column_number;
+        item.m_has_allocated_column = true;
+        m_columns_allocated_changed = true;
+      }
+      
       //Start the next child below this child, plus the padding
       column_child_y_start += item_height;
       column_child_y_start += m_row_padding; //Ignored if this is the last item - we will just start a new column when we find that column_child_y_start is too much.
@@ -896,7 +912,7 @@ void FlowTable::forall_vfunc(gboolean /* include_internals */, GtkCallback callb
 {
   for(type_vecChildren::const_iterator iter = m_children.begin(); iter != m_children.end(); ++iter)
   {
-    FlowTableItem item = *iter;
+    const FlowTableItem& item = *iter;
 
     Gtk::Widget* first = item.m_first;
     if(first)
@@ -1053,5 +1069,26 @@ bool FlowTable::on_expose_event(GdkEventExpose* event)
 #endif
 }
 
+bool FlowTable::get_column_for_first_widget(const Gtk::Widget& first, guint& column)
+{
+  //Initialize output parameter:
+  column = 0;
+  
+  for(type_vecChildren::const_iterator iter = m_children.begin(); iter != m_children.end(); ++iter)
+  {
+    const FlowTableItem& item = *iter;
+
+    if((&first == item.m_first) && item.m_has_allocated_column)
+    {
+       column = item.m_allocated_column;
+       return true;
+    }
+  }
+  
+  return false;
+}
+
+
+
 } //namespace Glom
 
diff --git a/glom/utility_widgets/flowtable.h b/glom/utility_widgets/flowtable.h
index 3f7aff7..04b990d 100644
--- a/glom/utility_widgets/flowtable.h
+++ b/glom/utility_widgets/flowtable.h
@@ -69,6 +69,12 @@ public:
   // Implement forall which is not implemented in gtkmm:
   typedef sigc::slot<void, Widget&> ForallSlot;
   void forall(const ForallSlot& slot);
+  
+  /** Get the column in which the specified "first" widget is placed.
+   * result false if the widget is not one of the "first" widgets, or 
+   * if has not yet been placed in a column, because the size has not yet been requested.
+   */
+  bool get_column_for_first_widget(const Gtk::Widget& first, guint& column);
 
 private:
 
@@ -88,19 +94,22 @@ private:
 
   //Handle child widgets:
   virtual void on_size_request(Gtk::Requisition* requisition);
-  virtual void on_size_allocate(Gtk::Allocation& allocation);
   virtual GType child_type_vfunc() const;
   virtual void on_add(Gtk::Widget* child);
   virtual void forall_vfunc(gboolean include_internals, GtkCallback callback, gpointer callback_data);
   virtual void on_remove(Gtk::Widget* child);
 
+
+protected:
+
+  virtual void on_size_allocate(Gtk::Allocation& allocation);
+  
   //Do extra drawing:
   //Virtual method overrides:
   void on_realize();
   void on_unrealize();
   bool on_expose_event(GdkEventExpose* event);
-
-protected:
+  
   int get_column_height(guint start_widget, guint widget_count, int& total_width) const;
 
   /** 
@@ -119,6 +128,10 @@ protected:
     bool m_expand_first_full;
     bool m_expand_second;
     
+    //The column that the widgets are currently in, due to the size/allocation.
+    bool m_has_allocated_column;
+    guint m_allocated_column;
+    
     bool operator==(Gtk::Widget* child) const
     {
       return (child == m_first || child == m_second);
@@ -144,6 +157,10 @@ private:
 protected:
   typedef std::vector<FlowTableItem> type_vecChildren;
   type_vecChildren m_children;
+  
+  //Reset this and check it later to see if the layout of items has changed.
+  bool m_columns_allocated_changed;
+  
 private:
   guint m_columns_count;
   guint m_column_padding, m_row_padding;



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