Hi Mike,

I thought I was doing something wrong, but from what I've been reading online, this is a GTK bug. But apparently it was supposed to have been fixed in 3.22, so I'm stumped. Perhaps I found a way to wake it up again?

Error:

Gtk-CRITICAL **: gtk_cell_layout_add_attribute: assertion 'GTK_IS_CELL_LAYOUT (cell_layout)' failed

What this code boils down to is... I'm dynamically populating a TreeView with a list of PgFontDescriptions. But this isn't in a ScrolledWindow yet and so it just goes on for 300+ lines off-screen. I did try putting it in a ScrolledWindow, to see if that would clear things up, but I it only showed two lines of the TreeView (with a scrollbar) and spit out a whole other set of errors:

(treeview_dynamic_fill_with_scrolled_window.exe:12156): Gtk-CRITICAL **: gtk_cell_layout_add_attribute: assertion 'GTK_IS_CELL_LAYOUT (cell_layout)' failed

(treeview_dynamic_fill_with_scrolled_window.exe:12156): Gtk-WARNING **: Allocating size to GtkWindow 0000021ebb85ae40 without calling gtk_widget_get_preferred_width/height(). How does the code know the size to allocate?

I'm finding no example code to guide me, so any help will be very much appreciated. Sorry for such a long message. I know you're likely tired after a day's work, so if it takes a while to answer, I get it. I'm pretty bagged myself ATM.

Without the scrolled window:

// Minimal TreeView with one column

import std.stdio;
import std.math;

import gtk.MainWindow;
import gtk.Main;
import gtk.Widget;
import gtk.Box;
import gtk.TreeView;
import gtk.TreeSelection;
import gtk.ListStore;
import gtk.TreeIter;
import gtk.TreePath;
import gtk.TreeViewColumn;
import gtk.CellRendererText;
import pango.PgCairoFontMap;
import pango.PgFontMap;
import pango.PgFontFamily;
import pango.PgFontDescription;

import singleton.S_FontList;

void main(string[] args)
{
	Main.init(args);
	
	TestRigWindow myTestRig = new TestRigWindow("Dynamically-filled TreeView");

	Main.run();
	
} // main()


class TestRigWindow : MainWindow
{
	AppBox appBox;
	
	this(string title)
	{
		super(title);
		
		addOnDestroy(&quitApp);
		
		appBox = new AppBox();
		add(appBox);
		
		showAll();

	} // this() CONSTRUCTOR
	
		
	void quitApp(Widget widget)
	{
		writeln("Bye.");
		Main.quit();
		
	} // quitApp()

} // class TestRigWindow


class AppBox : Box
{
	FontTreeView signTreeView;
	
	this()
	{
		super(Orientation.VERTICAL, 10);
		
		signTreeView = new FontTreeView();
		packStart(signTreeView, false, false, 0);
		
	} // this()

} // class AppBox


/*
 *A TreeView needs:
 * - at least one column, and
 * - a ListStore or TreeStore to serve as both data model (TreeModel) and storage (ListStore or TreeStore).
 */
class FontTreeView : TreeView
{
	FontFamilyColumn fontFamilyColumn;	// where the data is displayed
	FontSizeColumn fontSizeColumn;
	FontPangoSizeColumn fontPangoSizeColumn;
	FontStyleColumn fontStyleColumn;
	FontWeightColumn fontWeightColumn;
	FontListStore fontListStore;				// where the data is stored
	
	this()
	{
		super();
		addOnRowActivated(&onRowActivated);
		
		fontListStore = new FontListStore();
		setModel(fontListStore);
		
		fontFamilyColumn = new FontFamilyColumn(fontListStore);
		appendColumn(fontFamilyColumn);

		fontSizeColumn = new FontSizeColumn();
		appendColumn(fontSizeColumn);
		
		fontPangoSizeColumn = new FontPangoSizeColumn();
		appendColumn(fontPangoSizeColumn);
		
		fontStyleColumn = new FontStyleColumn();
		appendColumn(fontStyleColumn);
		
		fontWeightColumn = new FontWeightColumn();
		appendColumn(fontWeightColumn);
		
	} // this()
	
	
	void onRowActivated(TreePath treePath, TreeViewColumn tvc, TreeView tv)
	{
		int columnNumber;
		TreeIter treeIter = new TreeIter(fontListStore, treePath);
		
		// find the column number...
		if(tvc.getTitle() == "Sign Message")
		{
			columnNumber = 0;
		}
		else if(tvc.getTitle() == "Sign Description")
		{
			columnNumber = 1;
		}

		writeln("TreePath (row): ", treePath, " columnNumber: ", columnNumber);
		
		// get the contents of the cell double-clicked by the user
		// Because there are nothing but strings in the store, we don't have to
		// do any more digging, just echo the string to the terminal.
		auto value = fontListStore.getValue(treeIter, columnNumber);
		writeln("cell contains: ", value.getString());
		
	} // onRowActivated()
	
} // class FontTreeView


/*
 * A TreeViewColumn needs:
 * - a string that will become the title,
 * - at least one CellRenderer (with suffix Accel, Class, Combo, Pixbuf, Progress, Spin, Spinner, Text, or Toggle)
 * - a string description of the attribute (data) type
 * - and a column number (starting from 0)
 */
class FontFamilyColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Font Family";
	string attributeType = "text";
	int columnNumber = 0; // numbering starts at '0'

	this(FontListStore fontListStore)
	{
		cellRendererText = new CellRendererText();
		addAttribute(cellRendererText, "font-desc", fontListStore.Column.FONT_DESC);
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontFamilyColumn


class FontSizeColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Size";
	string attributeType = "text";
	int columnNumber = 1; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontSizeColumn


class FontPangoSizeColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Pango Units";
	string attributeType = "text";
	int columnNumber = 2; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontPangoSizeColumn


class FontStyleColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Style";
	string attributeType = "text";
	int columnNumber = 3; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontStyleColumn


class FontWeightColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Weight";
	string attributeType = "text";
	int columnNumber = 4; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontWeightColumn


/*
 * A ListStore needs:
 * - an array of GType types (essentially, data types such as string, int, etc.)
 *   so the constructor knows what's being stored, and
 * - a TreeIter for creating rows of data.
 * 
 * Rows are added to the ListStore with the setValue() function which needs:
 * - a TreeIter (could be thought of as the row number, but it's a pointer object)
 * - a column number, and
 * - the data to be stored.
 */
class FontListStore : ListStore
{
	SysFontListPango sysFontListPango;
	PgFontDescription[] fontList;
	TreeIter treeIter;
	
	enum Column
	{
		FAMILY = 0,
		SIZE,
		PANGO_SIZE,
		STYLE,
		WEIGHT,
		FONT_DESC
		
	} // enum Column
	
	/*
	 * DYNAMIC LOAD FROM FONTS
	 */
	this()
	{
		super([GType.STRING, GType.STRING, GType.STRING, GType.STRING, GType.STRING, PgFontDescription.getType()]);

		sysFontListPango = new SysFontListPango();
		fontList = sysFontListPango.getList();
		
		foreach(font; fontList)
		{
			treeIter = createIter();
			
			setValue(treeIter, 0, font.getFamily());
			setValue(treeIter, 1, font.getSize() / 1024);
			setValue(treeIter, 2, font.getSize());
			setValue(treeIter, 3, font.getStyle());
			setValue(treeIter, 4, font.getWeight());
			setValue(treeIter, 5, font);
		}

	} // this()

} // class FontListStore


class SysFontListPango
{
	private:
	PgFontMap _pgFontMap;
	PgFontFamily[] _pgFontFamilies;
	PgFontFamily _font;
	PgFontDescription[] _pgFontDescriptions;
	PgFontDescription _fontDesc;
	int _fontSize = 9, counter = 1;

	public:
	this()
	{	
		_pgFontMap = PgCairoFontMap.getDefault();
		_pgFontMap.listFamilies(_pgFontFamilies);

		counter = 1;
		
		foreach(_font; _pgFontFamilies)
		{
			_fontDesc = new PgFontDescription(_font.getName(), _fontSize);
			_pgFontDescriptions ~= _fontDesc;

			if(fmod(counter, 4) == 0)
			{
				varyFontBySize();
			}

			if(fmod(counter, 5) == 0)
			{
				varyFontByWeight(PangoWeight.BOLD);
			}
			else if(fmod(counter, 6) == 0)
			{
				varyFontByWeight(PangoWeight.THIN);
			}

			_fontSize++;
			counter++;
	
			if(_fontSize > 19)
			{
					_fontSize = 9;
			}
		}

	} // this()
	
	
	void varyFontBySize()
	{
		_fontDesc.setStyle(PangoStyle.ITALIC);
		
	} // varyFontBySize()
	
	
	void varyFontByWeight(PangoWeight weight)
	{
		_fontDesc.setWeight(weight);
		
	} // varyFontByWeight()


	PgFontDescription[] getList()
	{
		return(_pgFontDescriptions);
		
	} // getList()
	
	
	void printList()
	{
		int counter = 1;
		
		foreach(_fontDesc; _pgFontDescriptions)
		{
			writeln("counter: ", counter);
			writeln("family: ", _fontDesc.getFamily());
			writeln("gravity: ", _fontDesc.getGravity());
			writeln("size in points: ", _fontDesc.getSize() / 1024); // pango _font units are 1024 x points
			writeln("size in Pango units: ", _fontDesc.getSize()); // pango _font units are 1024 x points		
			writeln("stretch: ", _fontDesc.getStretch());
			writeln("style: ", _fontDesc.getStyle());
			writeln("variant: ", _fontDesc.getVariant());
			writeln("weight: ", _fontDesc.getWeight());
			writeln();
			counter++;

		} // foreach

	} // printList()
	
} // class SysFontListPango

with the scrolled window:

// Minimal TreeView with one column

import std.stdio;
import std.math;

import gtk.MainWindow;
import gtk.Main;
import gtk.Widget;
import gtk.Box;
import gtk.ScrolledWindow;
import gtk.TreeView;
import gtk.TreeSelection;
import gtk.ListStore;
import gtk.TreeIter;
import gtk.TreePath;
import gtk.TreeViewColumn;
import gtk.CellRendererText;
import pango.PgCairoFontMap;
import pango.PgFontMap;
import pango.PgFontFamily;
import pango.PgFontDescription;

import singleton.S_FontList;

void main(string[] args)
{
	Main.init(args);
	
	TestRigWindow myTestRig = new TestRigWindow("Dynamically-filled TreeView");

	Main.run();
	
} // main()


class TestRigWindow : MainWindow
{
	AppBox appBox;
	
	this(string title)
	{
		super(title);
		setSizeRequest(750, 400);
		
		addOnDestroy(&quitApp);
		
		appBox = new AppBox();
		add(appBox);
		
		showAll();

	} // this() CONSTRUCTOR
	
		
	void quitApp(Widget widget)
	{
		writeln("Bye.");
		Main.quit();
		
	} // quitApp()

} // class TestRigWindow


class AppBox : Box
{
	MyScrolledWindow myScrolledWindow;
	
	this()
	{
		super(Orientation.VERTICAL, 10);

		myScrolledWindow = new MyScrolledWindow();
		packStart(myScrolledWindow, false, false, 0);
		
	} // this()

} // class AppBox


class MyScrolledWindow : ScrolledWindow
{
	FontTreeView fontTreeView;
	
	this()
	{
		fontTreeView = new FontTreeView();
		super();
		add(fontTreeView);
		
	} // this()
	
} // class MyScrolledWindow

/*
 *A TreeView needs:
 * - at least one column, and
 * - a ListStore or TreeStore to serve as both data model (TreeModel) and storage (ListStore or TreeStore).
 */
class FontTreeView : TreeView
{
	FontFamilyColumn fontFamilyColumn;	// where the data is displayed
	FontSizeColumn fontSizeColumn;
	FontPangoSizeColumn fontPangoSizeColumn;
	FontStyleColumn fontStyleColumn;
	FontWeightColumn fontWeightColumn;
	FontListStore fontListStore;				// where the data is stored
	
	this()
	{
		super();
		addOnRowActivated(&onRowActivated);
		
		fontListStore = new FontListStore();
		setModel(fontListStore);
		
		fontFamilyColumn = new FontFamilyColumn(fontListStore);
		appendColumn(fontFamilyColumn);

		fontSizeColumn = new FontSizeColumn();
		appendColumn(fontSizeColumn);
		
		fontPangoSizeColumn = new FontPangoSizeColumn();
		appendColumn(fontPangoSizeColumn);
		
		fontStyleColumn = new FontStyleColumn();
		appendColumn(fontStyleColumn);
		
		fontWeightColumn = new FontWeightColumn();
		appendColumn(fontWeightColumn);
		
	} // this()
	
	
	void onRowActivated(TreePath treePath, TreeViewColumn tvc, TreeView tv)
	{
		int columnNumber;
		TreeIter treeIter = new TreeIter(fontListStore, treePath);
		
		// find the column number...
		if(tvc.getTitle() == "Sign Message")
		{
			columnNumber = 0;
		}
		else if(tvc.getTitle() == "Sign Description")
		{
			columnNumber = 1;
		}

		writeln("TreePath (row): ", treePath, " columnNumber: ", columnNumber);
		
		// get the contents of the cell double-clicked by the user
		// Because there are nothing but strings in the store, we don't have to
		// do any more digging, just echo the string to the terminal.
		auto value = fontListStore.getValue(treeIter, columnNumber);
		writeln("cell contains: ", value.getString());
		
	} // onRowActivated()
	
} // class FontTreeView


/*
 * A TreeViewColumn needs:
 * - a string that will become the title,
 * - at least one CellRenderer (with suffix Accel, Class, Combo, Pixbuf, Progress, Spin, Spinner, Text, or Toggle)
 * - a string description of the attribute (data) type
 * - and a column number (starting from 0)
 */
class FontFamilyColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Font Family";
	string attributeType = "text";
	int columnNumber = 0; // numbering starts at '0'

	this(FontListStore fontListStore)
	{
		cellRendererText = new CellRendererText();
		addAttribute(cellRendererText, "font-desc", fontListStore.Column.FONT_DESC);
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontFamilyColumn


class FontSizeColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Size";
	string attributeType = "text";
	int columnNumber = 1; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontSizeColumn


class FontPangoSizeColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Pango Units";
	string attributeType = "text";
	int columnNumber = 2; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontPangoSizeColumn


class FontStyleColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Style";
	string attributeType = "text";
	int columnNumber = 3; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontStyleColumn


class FontWeightColumn : TreeViewColumn
{
	CellRendererText cellRendererText;
	string columnTitle = "Weight";
	string attributeType = "text";
	int columnNumber = 4; // numbering starts at '0'

	this()
	{
		cellRendererText = new CellRendererText();
		
		super(columnTitle, cellRendererText, attributeType, columnNumber);
		
	} // this()

} // class FontWeightColumn


/*
 * A ListStore needs:
 * - an array of GType types (essentially, data types such as string, int, etc.)
 *   so the constructor knows what's being stored, and
 * - a TreeIter for creating rows of data.
 * 
 * Rows are added to the ListStore with the setValue() function which needs:
 * - a TreeIter (could be thought of as the row number, but it's a pointer object)
 * - a column number, and
 * - the data to be stored.
 */
class FontListStore : ListStore
{
	SysFontListPango sysFontListPango;
	PgFontDescription[] fontList;
	TreeIter treeIter;
	
	enum Column
	{
		FAMILY = 0,
		SIZE,
		PANGO_SIZE,
		STYLE,
		WEIGHT,
		FONT_DESC
		
	} // enum Column
	
	/*
	 * DYNAMIC LOAD FROM FONTS
	 */
	this()
	{
		super([GType.STRING, GType.STRING, GType.STRING, GType.STRING, GType.STRING, PgFontDescription.getType()]);

		sysFontListPango = new SysFontListPango();
		fontList = sysFontListPango.getList();
		
		foreach(font; fontList)
		{
			treeIter = createIter();
			
			setValue(treeIter, 0, font.getFamily());
			setValue(treeIter, 1, font.getSize() / 1024);
			setValue(treeIter, 2, font.getSize());
			setValue(treeIter, 3, font.getStyle());
			setValue(treeIter, 4, font.getWeight());
			setValue(treeIter, 5, font);
		}

	} // this()

} // class FontListStore


class SysFontListPango
{
	private:
	PgFontMap _pgFontMap;
	PgFontFamily[] _pgFontFamilies;
	PgFontFamily _font;
	PgFontDescription[] _pgFontDescriptions;
	PgFontDescription _fontDesc;
	int _fontSize = 9, counter = 1;

	public:
	this()
	{	
		_pgFontMap = PgCairoFontMap.getDefault();
		_pgFontMap.listFamilies(_pgFontFamilies);

		counter = 1;
		
		foreach(_font; _pgFontFamilies)
		{
			_fontDesc = new PgFontDescription(_font.getName(), _fontSize);
			_pgFontDescriptions ~= _fontDesc;

			if(fmod(counter, 4) == 0)
			{
				varyFontBySize();
			}

			if(fmod(counter, 5) == 0)
			{
				varyFontByWeight(PangoWeight.BOLD);
			}
			else if(fmod(counter, 6) == 0)
			{
				varyFontByWeight(PangoWeight.THIN);
			}

			_fontSize++;
			counter++;
	
			if(_fontSize > 19)
			{
					_fontSize = 9;
			}
		}

	} // this()
	
	
	void varyFontBySize()
	{
		_fontDesc.setStyle(PangoStyle.ITALIC);
		
	} // varyFontBySize()
	
	
	void varyFontByWeight(PangoWeight weight)
	{
		_fontDesc.setWeight(weight);
		
	} // varyFontByWeight()


	PgFontDescription[] getList()
	{
		return(_pgFontDescriptions);
		
	} // getList()
	
	
	void printList()
	{
		int counter = 1;
		
		foreach(_fontDesc; _pgFontDescriptions)
		{
			writeln("counter: ", counter);
			writeln("family: ", _fontDesc.getFamily());
			writeln("gravity: ", _fontDesc.getGravity());
			writeln("size in points: ", _fontDesc.getSize() / 1024); // pango _font units are 1024 x points
			writeln("size in Pango units: ", _fontDesc.getSize()); // pango _font units are 1024 x points		
			writeln("stretch: ", _fontDesc.getStretch());
			writeln("style: ", _fontDesc.getStyle());
			writeln("variant: ", _fontDesc.getVariant());
			writeln("weight: ", _fontDesc.getWeight());
			writeln();
			counter++;

		} // foreach

	} // printList()
	
} // class SysFontListPango