/* * TTK Demonstration Caclulator * * This is non-functional mockup code for a calculator. * The current goal of TTK development is to turn this * into functioning code with as few changes to this * code as possible. * */ #include "gui/ttk/ttk.h" /* * For simplicity, and because some core calls * will make use of it, we're going to keep our * app object up here in a global. */ ttk_application * app; /* * This is the callback for the menu item File->Exit * When the run loop detects a completed click on * the exit menu item, it calls this function. * * ttk_quit() is not truly immediate, it will trigger * a series of shutdown routines and let the run loop * flush some events. There might also be toolkit-bound * functions to run on exit, which could even cancel * the entire quit call - for example, a "You have unsaved * work" dialog would cancel the quit and give you time * to answer the dialog, save your work, keep editing, etc. */ void menu_file_exit_func(ttk_menu_item * caller) { /* * Assuming the caller is a menu_item is reasonable, * but if you find yourself using bindings like this * on multiple objects, it's better to call it an object. * It doesn't matter here, since we're not actually * using the caller for anything, we just quit. */ ttk_quit(app); } /* * This is a generic button callback handler, so * all of our core calculator buttons can use * the same callback handler, which just appends * some input to a state machine that is the real * "calculator". Using generic handlers is also * useful for when you have dynamically generated * the calling objects, like a "recently used files" * list in a menu - the tag can contain the full path * to the file while the label displays a short version * and each menu item can point to the same handler. */ void calculator_button_func(ttk_button * caller) { /* * There are some guarantees about how this will be called; * for example, we'll always have something as a caller. * * Read the tag from the caller. */ char * tag = ttk_object_tag(caller); if (!tag) { /* Well that's odd! but better safe than sorry. */ return; } while (*tag) { /* * Add each character from the tag to the state machine; * since we're adding all of them, we could make * buttons with macros like 'pi' by making the tag * '3.1415926'... */ calculator_state_append(*tag); ++tag; } } /* * C main */ int main(int argc, char * argv[]) { /* * Initialize the TTK app "engine". * This will perform some of the initial set up of the * windowing environment and give us a context to work in. */ app = ttk_application_new(); /* * Create the base window for our calculator. * The standard constructor takes just a title. */ ttk_window * calculator_window = ttk_window_new("Calculator"); /* * Core content of the window is stored in a grid object. * A grid is one of the standard layout primitives. * +---------+ * |File + * +---------+ * | 003.1415| * +---------+ * |{buttons}| * +---------+ */ ttk_grid * window_main = ttk_grid_new(3, 1); /* Three vertical, one horizonta */ /* Menu Bar */ ttk_menu_bar * menu_bar = ttk_menu_bar_new(); ttk_menu * menu_file = ttk_menu_new("File", NULL); ttk_menu_item * menu_file_exit = ttk_menu_item_new("Exit", menu_file_exit_func); ttk_menu_add_item(menu_file, menu_file_exit); ttk_menu_bar_add(menu_bar, menu_file); ttk_grid_add(window_main, menu_bar, TTK_COLLAPSE_FIT); /* Text Field */ ttk_text_field * text_field = ttk_field_new("0.00"); ttk_text_field_set_alignment(text_field, TTK_ALIGN_RIGHT); ttk_grid_add(window_main, text_field, TTK_COLLAPSE_FIT); /* * If we started up the app right now, we'd see a window * with a menu bar and a text field, and a small gap space * at the bottom depending on how we resize the window, as * the default empty cell at the bottom of the grid has * the sizing property TTK_EXPAND_FIT while our * menu and text field rows are set to collapse. */ /* Grid of buttons * +-+-+-+-+ * |7|8|9|+| * +-+-+-+-+ * |4|5|6|-| * +-+-+-+-+ * |1|2|3|/| * +-+-+-+-+ * |0|.|*|=| * +-+-+-+-+ */ ttk_grid * button_grid = ttk_grid_new(4, 4); /* * We could do each of these explicitly, but this is a good example * of how to add tags to widgets that can be retrieved by callbacks * * We'll make two arrays, one for the text on the button, and one * for the "tag" applied to the button. * * To demonstrate why the tags are "necessary", I'll label some * of the buttons in Japanese. */ char * button_labels[] = { "7","八","9","+", "4","五","6","-", "1","二","3","/", "0",".", "*","=", NULL }; char * button_tags[] = { "7","8","9","+", "4","5","6","-", "1","2","3","/", "0",".","*","=", NULL }; for (int i = 0; button_labels[i] != NULL; ++i) { /* Create the button */ ttk_button * button = ttk_button_new(button_labels[i], calculator_button_func); /* Set the tag */ ttk_object_tag_set(button, button_tags[i]); /* * Add it to the grid; grids fill like English-language text, * starting at the top row and filling left to right. */ ttk_grid_add(button_grid, button, TTK_EVEN_FIT); } /* Add the button grid to the core layout grid */ ttk_grid_add(window_main, button_grid, TTK_EXPAND_FIT); /* Set the window_main grid as the core content of the window */ ttk_window_content_set(calculator_window, window_main); /* * Once we've finished building the window, we * append it to the list of windows in use by the * application so that when we launch the run loop * it will be active and displayed. */ ttk_app_add_window(app, calculator_window); /* * ttk_run enters the main loop, and then we just * wait for it to finish. Library functions provide * for setting the return value from the entire * application, which will be returned by the loop * function when we're all finished. */ return ttk_run(app); }