neogfx Cross-Platform C++ GUI Library -- Coming Soon

The supported platforms of v1.0 will be Windows desktop and Android.

Initial design goals will be:

1) to be as fast as possible (after all this is C++);
2) to embrace modern C++ paradigms (which includes templates and exceptions);
3) for compiled binaries to be as small as possible with no huge redistributables (after all this is not Qt);
4) to not use signals and slots as an event mechanism (after all this is not Qt).

The major unknown at this point is the quality of C++ support provided by the Android (NDK) platform; currently third party libraries/patches seem to be required to enable C++ exception support and to provide for a C++ Standard Library (STL): come on Google, get with the program! :)

Watch this space for further updates.

Sample application:

Try the sample application for yourself (requires Windows 7, Vista or XP):

Sample application source code:

<?xml version="1.0" encoding="utf-8"?>
<neogfx>
  <resources name="test" language="en-gb">
    <main_window title="ClicksAndWhistles 3.0" layout_type="horizontal" layout_margin="0.5em" layout_gap="0.5em" x="2in" y="2in" width="7in" height="8in">
      <menu mnemonics="auto">
        <menu name="File">
          <command text="Connect..." shortcut="Ctrl+N" id="connect" />
          <command text="Join Channel..." shortcut="Ctrl+J" id="join_channel" />
          <command text="Disconnect" shortcut="Ctrl+Q" id="disconnect" />
          <command text="New Console" id="new" />
          <separator />
          <command text="Manage Identities..." id="manage_identities" />
          <command text="Manage Servers..." id="manage_servers" />
          <separator />
          <command text="View Log..." id="view_log" />
          <separator />
          <command text="Recent Server" id="recent_server" disabled="true" />
          <separator />
          <command text="Exit" id="exit" />
        </menu>
        <menu name="View">
          <command text="Toolbar" id="view_toolbar" />
          <command text="Status Bar" id="view_status_bar" />
        </menu>
        <menu name="Favourites">
          <command text="Add to Favourites..." id="add_to_favourites" />
          <command text="Organize Favourites..." id="organize_favourites" />
          <separator />
          <command text="עִבְרִית العربية" />
          <command text="one" />
          <command text="two" />
          <command text="three" />
          <menu name="folder 1">
            <command text="apples" />
            <command text="oranges" />
            <command text="bananas" />
            <menu name="folder">
              <command text="circles" />
              <command text="squares" />
              <command text="triangles" />
              <command text="hexagons" />
              <command text="pentagons" />
              <command text="octagons" />
            </menu>
            <command text="grapes" />
            <command text="peaches" />
            <command text="lemons" />
          </menu>
          <menu name="folder 2">
            <command text="a" />
            <command text="b" />
            <command text="c" />
            <menu name="folder">
              <command text="aa" />
              <command text="bb" />
              <command text="cc" />
              <command text="dd" />
              <command text="ee" />
              <command text="ff" />
            </menu>
            <command text="d" />
            <command text="e" />
            <command text="f" />
          </menu>
          <command text="four" />
          <command text="five" />
          <command text="six" />
        </menu>
        <menu name="Tools">
          <command text="Options..." id="options" />
          <command text="Macros..." id="macros" />
          <command text="Keywords and Emoticons..." id="keywords" />
          <command text="Plugins..." id="plugins" />
          <separator />
          <command text="Install Program Updates..." id="update_program" />
        </menu>
        <menu name="Help">
          <command text="User Guide..." id="user_guide" />
          <command text="ClicksAndWhistles Channel" id="help_channel" />
          <separator />
          <command text="About ClicksAndWhistles..." id="about" />
        </menu>
      </menu>
      <layout id ="dialog" gap="0.25em" margin="0.5em" valign="centre">
        <list_box id="items" height="240px">
          <column name="Entry" />
          <column name="Some Text" />
          <column name="Some More Text" />
        </list_box>
        <tree_box id ="tree_items" height="240px" show_buttons="true">
          <column name="Entry" />
          <column name="Some Text" />
          <column name="Some More Text" />
        </tree_box>
        <button id="one" text="Button One" />
        <button id="two" text="Button Two" />
        <edit_box id="edit" text="abc123" label="Text:" width="1.5in"/>
        <button id="three" text="Button Three" />
        <layout type="grid" gap="0.25em" columns="3" rows="4">
          <button id="keypad1" text="1" />
          <button id="keypad2" text="2" />
          <button id="keypad3" text="3" />
          <button id="keypad4" text="4" />
          <button id="keypad5" text="5" />
          <button id="keypad6" text="6" />
          <button id="keypad7" text="7" />
          <button id="keypad8" text="8" />
          <button id="keypad9" text="9" />
          <spacer />
          <button id="keypad0" text="0" />
          <spacer />
        </layout>
        <button id="big" text="This is a fixed width multiline button." multiline="true" width="2in" />
        <button id="rtl" text="Another multiline button with some RTL text.\nעִבְרִית\nالعربية" multiline="true" />
      </layout>
      <separator type="vertical" />
      <layout type="vertical" gap="0.5em" margin="0.5em" align="fill">
        <button id="red" text="Red" colour="#802020" />
        <button id="green" text="Green" colour="#208020" />
        <button id="blue" text="Blue" colour="#202080" />
        <spacer />
        <spacer />
        <button id="goldenrod" text="Goldenrod" colour="goldenrod"/>
        <button id="khaki" text="Khaki" colour="khaki"/>
        <spacer />
        <button id="hotpink" text="Hot Pink" colour="hotpink"/>
      </layout>
    </main_window>
  </resources>
</neogfx>
#include <neolib/string.h>
#include <neolib/random.h>
#include <neogfx/neogfx.h>
#include <neogfx/app.h>
#include <neogfx/command.h>
#include <neogfx/window.h>
#include <neogfx/button.h>
#include <neogfx/edit_box.h>
#include <neogfx/spacer.h>
#include <neogfx/separator.h>
#include <neogfx/list_box.h>
#include <neogfx/tree_box.h>
#include "resources.h"

typedef neogfx::three_column_list_box test_list_box;
typedef neogfx::three_column_tree_box test_tree_box;

class example_event_handler : 
	public neogfx::command_event_sink,
	public neogfx::window_event_sink, 
	public neogfx::button_event_sink, 
	public neogfx::list_box_event_sink, 
	public neogfx::tree_box_event_sink
{
public:
	example_event_handler(neogfx::app& aApp) : iApp(aApp) 
	{
		on_command(iApp, test::id_exit, &example_event_handler::on_exit);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_one), &example_event_handler::change_theme_colour_and_other_stuff);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_two), &example_event_handler::no_layout);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_three), &example_event_handler::flow_layout);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_keypad1), &example_event_handler::disable_list_box_full_row_select);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_keypad2), &example_event_handler::enable_list_box_full_row_select);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_keypad3), &example_event_handler::disable_tree_box_full_row_select);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_keypad4), &example_event_handler::enable_tree_box_full_row_select);
		on_button_press_event(iApp.window_from_id<neogfx::button>(test::id_keypad0), &example_event_handler::black_theme);
		neogfx::list_box& itemsListBox = iApp.window_from_id<neogfx::list_box>(test::id_items);
		on_list_box_event(itemsListBox, neogfx::list_box_event::ItemSelected, &example_event_handler::list_box_item_selected);
		on_list_box_event(itemsListBox, neogfx::list_box_event::ItemDraw, &example_event_handler::list_box_draw_item);
		on_list_box_event(itemsListBox, neogfx::list_box_event::ItemMenu, &example_event_handler::list_box_delete_item);
		on_list_box_event(itemsListBox, neogfx::list_box_event::HeaderAction, &example_event_handler::list_box_header_action);
		neogfx::tree_box& itemsTreeBox = iApp.window_from_id<neogfx::tree_box>(test::id_tree_items);
		on_tree_box_event(itemsTreeBox, neogfx::tree_box_event::ItemSelected, &example_event_handler::tree_box_item_selected);
		on_tree_box_event(itemsTreeBox, neogfx::tree_box_event::ItemDraw, &example_event_handler::tree_box_draw_item);
		on_tree_box_event(itemsTreeBox, neogfx::tree_box_event::ItemMenu, &example_event_handler::tree_box_delete_item);
		on_tree_box_event(itemsTreeBox, neogfx::tree_box_event::HeaderAction, &example_event_handler::tree_box_header_action);
	}
	
	void on_exit()
	{
		iApp.exit(0);
	}

	void change_theme_colour_and_other_stuff()
	{
		iApp.current_theme() = neogfx::theme(iApp, neogfx::colour(iRandom.get(0xFF), iRandom.get(0xFF), iRandom.get(0xFF)));
		iApp.window_from_id(test::id_one).set_window_text(neogfx::text(L"Button One ") + neogfx::text(iRandom.get(31), L'.'));
	}
	
	void black_theme()
	{
		iApp.current_theme() = neogfx::theme(iApp, neogfx::colour::Black);
	}

	void no_layout()
	{
		iApp.window_from_id<neogfx::layout>(test::id_dialog).set_layout_type(neogfx::LayoutNone);
	}
	
	void flow_layout()
	{
		iApp.window_from_id<neogfx::layout>(test::id_dialog).set_layout_type(neogfx::LayoutFlow);
	}	

	void disable_list_box_full_row_select()
	{
		iApp.window_from_id<test_list_box>(test::id_items).disable_full_row_select();
	}

	void enable_list_box_full_row_select()
	{
		iApp.window_from_id<test_list_box>(test::id_items).enable_full_row_select();
	}

	void disable_tree_box_full_row_select()
	{
		iApp.window_from_id<test_tree_box>(test::id_tree_items).disable_full_row_select();
	}

	void enable_tree_box_full_row_select()
	{
		iApp.window_from_id<test_tree_box>(test::id_tree_items).enable_full_row_select();
	}

	void list_box_item_selected(const neogfx::list_box_event& aEvent)
	{
	}

	void list_box_draw_item(const neogfx::list_box_event& aEvent)
	{
		test_list_box& itemsListBox = iApp.window_from_id<test_list_box>(test::id_items);
		const neogfx::list_box_event::draw_item_info& drawItemInfo = aEvent.data();
		if (!itemsListBox.has_selection() || !itemsListBox.has_focus() || itemsListBox.selection() != aEvent.item_index() || (!itemsListBox.full_row_select() && 
			drawItemInfo.column_index() != 0))
		{	// if list box item is not selected then make the item text a random colour (seed = item data)
			neolib::random random(reinterpret_cast<int>(itemsListBox[aEvent.item_index()]));
			neogfx::colour randomColour = neogfx::colour(random.get(0xFF), random.get(0xFF), random.get(0xFF));
			while(randomColour.similar_intensity(itemsListBox.window_colour(), 0x20))
				randomColour = neogfx::colour(random.get(0xFF), random.get(0xFF), random.get(0xFF));
			drawItemInfo.set_text_colour(randomColour);
		}
	}
	
	void list_box_delete_item(const neogfx::list_box_event& aEvent)
	{
		test_list_box& itemsListBox = iApp.window_from_id<test_list_box>(test::id_items);
		itemsListBox.erase(aEvent.item_index());
	}
	
	struct list_box_sorter
	{
		bool operator()(const test_list_box::item& aLeft, const test_list_box::item& aRight) const
		{
			return reinterpret_cast<int>(aLeft.data()) < reinterpret_cast<int>(aRight.data());
		}
	};

	void list_box_header_action(const neogfx::list_box_event& aEvent)
	{
		test_list_box& itemsListBox = iApp.window_from_id<test_list_box>(test::id_items);
		if (aEvent.column_index() != 0)
			itemsListBox.sort_by_cell_text(aEvent.column_index(), true, true);
		else
			itemsListBox.sort(0, list_box_sorter());
	}
	
	void tree_box_item_selected(const neogfx::tree_box_event& aEvent)
	{
	}

	void tree_box_draw_item(const neogfx::tree_box_event& aEvent)
	{
		test_tree_box& itemsTreeBox = iApp.window_from_id<test_tree_box>(test::id_tree_items);
		const neogfx::tree_box_event::draw_item_info& drawItemInfo = aEvent.data();
		if (!itemsTreeBox.has_selection() || !itemsTreeBox.has_focus() || itemsTreeBox.selection() != aEvent.location() || (!itemsTreeBox.full_row_select() && 
			drawItemInfo.column_index() != 0))
		{	// if tree box item is not selected then make the item text a random colour (seed = item data)
			neolib::random random(reinterpret_cast<int>(static_cast<const test_tree_box::item&>(aEvent.item()).data()));
			neogfx::colour randomColour = neogfx::colour(random.get(0xFF), random.get(0xFF), random.get(0xFF));
			while(randomColour.similar_intensity(itemsTreeBox.window_colour(), 0x20))
				randomColour = neogfx::colour(random.get(0xFF), random.get(0xFF), random.get(0xFF));
			drawItemInfo.set_text_colour(randomColour);
		}
	}
	
	void tree_box_delete_item(const neogfx::tree_box_event& aEvent)
	{
		test_tree_box& itemsTreeBox = iApp.window_from_id<test_tree_box>(test::id_tree_items);
		itemsTreeBox.erase(aEvent.location());
	}
	
	struct tree_box_sorter
	{
		bool operator()(const test_tree_box::item& aLeft, const test_tree_box::item& aRight) const
		{
			return reinterpret_cast<int>(aLeft.data()) < reinterpret_cast<int>(aRight.data());
		}
	};
			
	void tree_box_header_action(const neogfx::tree_box_event& aEvent)
	{
		test_tree_box& itemsTreeBox = iApp.window_from_id<test_tree_box>(test::id_tree_items);
		if (aEvent.column_index() != 0)
			itemsTreeBox.sort_by_cell_text(aEvent.column_index(), true, true);
		else
			itemsTreeBox.sort(0, tree_box_sorter());
	}
private:
	neogfx::app& iApp;
	neolib::random iRandom;
};

void populate_tree(test_tree_box& aTree, test_tree_box::const_iterator aParentNode)
{
	if (aTree.depth(aParentNode) == 4)
		return;
	static neolib::random random;
	for (int i = random.get(0, 10); i != 0; --i)
	{
		test_tree_box::const_iterator newNode = aTree.append(aParentNode, reinterpret_cast<void*>(aTree.size() + 1),  
			neogfx::text(L"Tree box entry #") + neolib::integer_to_string<wchar_t>(aTree.size() + 1));
		for (int j = 1; j != 3; ++j)
		{
			const neogfx::text& letters = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
			neogfx::text testText;
			for (int k = random.get(1, 32); k != 0; --k)
				testText += letters[random.get(letters.size() - 1)];
			aTree.set_text(newNode, j, testText);
		}
		populate_tree(aTree, newNode);
	}
}

int main()
{
	neogfx::app myApp(test::resource_data);
	test_list_box& itemsListBox = myApp.window_from_id<test_list_box>(test::id_items);
	neolib::random random;
	for (int i = 1; i <= 10000; ++i)
	{
		test_list_box::const_iterator item = itemsListBox.append(reinterpret_cast<void*>(i), 
			neogfx::text(L"List box entry #") + neolib::integer_to_string<wchar_t>(i));
		for (int j = 1; j != 3; ++j)
		{
			const neogfx::text& letters = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
			neogfx::text testText;
			for (int k = random.get(1, 32); k != 0; --k)
				testText += letters[random.get(letters.size() - 1)];
			itemsListBox.set_text(item, j, testText);
		}
	}
	itemsListBox.sort_by_cell_text(1, true, true);
	test_tree_box& itemsTreeBox = myApp.window_from_id<test_tree_box>(test::id_tree_items);
	populate_tree(itemsTreeBox, itemsTreeBox.begin());
	itemsTreeBox.sort_by_cell_text(1, true, true);
	example_event_handler myExampleEventHandler(myApp);
	return myApp.run();
}

The neogfx C++ Coding Style Guide is here.

Windows is a registered trademark of Microsoft Corporation.
Android is a trademark of Google Inc.
Qt is a trademark of Nokia Corporation.