| /* |
| * Copyright 2019 University of Toronto |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * Authors: Mario Badr, Sameh Attia and Tanner Young-Schultz |
| */ |
| |
| #ifndef EZGL_APPLICATION_HPP |
| #define EZGL_APPLICATION_HPP |
| |
| #include "ezgl/canvas.hpp" |
| #include "ezgl/control.hpp" |
| #include "ezgl/callback.hpp" |
| #include "ezgl/graphics.hpp" |
| #include "ezgl/color.hpp" |
| |
| #include <map> |
| #include <memory> |
| #include <string> |
| |
| #include <gtk/gtk.h> |
| |
| /** |
| * A library for creating a graphical user interface. |
| */ |
| namespace ezgl { |
| |
| class application; |
| |
| /** |
| * The signature of a function that connects GObject to functions via signals. |
| * |
| * @see application::get_object. |
| */ |
| using connect_g_objects_fn = void (*)(application *app); |
| |
| /** |
| * The signature of a setup callback function |
| */ |
| using setup_callback_fn = void (*)(application *app, bool new_window); |
| |
| /** |
| * The signature of a button callback function |
| */ |
| using button_callback_fn = void (*)(GtkWidget *widget, application *app); |
| |
| /** |
| * The signature of a user-defined callback function for mouse events |
| */ |
| using mouse_callback_fn = void (*)(application *app, GdkEventButton *event, double x, double y); |
| |
| /** |
| * The signature of a user-defined callback function for keyboard events |
| */ |
| using key_callback_fn = void (*)(application *app, GdkEventKey *event, char *key_name); |
| |
| /** |
| * The core application. |
| * |
| * The GUI of an application is created from an XML file. Widgets created in the XML file can be retrieved from an |
| * application object, but only after the object has been initialized by GTK via application::run. |
| */ |
| class application { |
| public: |
| /** |
| * Configuration settings for the application. |
| * |
| * The GUI will be built from the XML description given by main_ui_resource. |
| * The XML file must contain a GtkWindow with the name in window_identifier. |
| */ |
| struct settings { |
| /** |
| * The resource path that contains the XML file, which describes the GUI. |
| */ |
| std::string main_ui_resource; |
| |
| /** |
| * The name of the main window in the XML file. |
| */ |
| std::string window_identifier; |
| |
| /** |
| * The name of the main canvas in the XML file. |
| */ |
| std::string canvas_identifier; |
| |
| /** |
| * Specify the function that will connect GUI objects to user-defined callbacks. |
| * |
| * GUI objects (i.e., a GObject) can be retrieved from this application object. These objects can then be connected |
| * to specific events using g_signal_connect. A list of signals that can be used to make these connections can be |
| * found <a href = "https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget.signals">here</a>. |
| * |
| * If not provided, application::register_default_buttons_callbacks function will be used, which assumes that the |
| * UI has GtkButton widgets named "ZoomFitButton", "ZoomInButton", "ZoomOutButton", "UpButton", "DownButton", |
| * "LeftButton", "RightButton", "ProceedButton" |
| */ |
| connect_g_objects_fn setup_callbacks; |
| |
| /** |
| * Create the settings structure with default values |
| */ |
| settings() |
| : main_ui_resource("/ezgl/main.ui"), window_identifier("MainWindow"), canvas_identifier("MainCanvas"), setup_callbacks(nullptr) |
| { |
| } |
| |
| /** |
| * Create the settings structure with user-defined values |
| */ |
| settings(std::string m_resource, std::string w_identifier, std::string c_identifier, connect_g_objects_fn s_callbacks = nullptr) |
| : main_ui_resource(m_resource), window_identifier(w_identifier), canvas_identifier(c_identifier), setup_callbacks(s_callbacks) |
| { |
| } |
| }; |
| |
| public: |
| /** |
| * Create an application. |
| * |
| * @param s The preconfigured settings. |
| */ |
| explicit application(application::settings s); |
| |
| /** |
| * Add a canvas to the application. |
| * |
| * If the canvas has already been added, it will not be overwritten and a warning will be displayed. |
| * |
| * @param canvas_id The id of the GtkDrawingArea in the XML file. |
| * @param draw_callback The function to call that draws to this canvas. |
| * @param coordinate_system The initial coordinate system of this canvas. |
| * @param background_color (OPTIONAL) The color of the canvas background. Default is WHITE. |
| * |
| * @return A pointer to the newly created canvas. |
| */ |
| canvas *add_canvas(std::string const &canvas_id, |
| draw_canvas_fn draw_callback, |
| rectangle coordinate_system, |
| color background_color = WHITE); |
| |
| /** |
| * Add a button |
| * |
| * @param button_text the new button text |
| * @param left the column number to attach the left side of the new button to |
| * @param top the row number to attach the top side of the new button to |
| * @param width the number of columns that the button will span |
| * @param height the number of rows that the button will span |
| * @param button_func callback function for the button |
| * |
| * The function assumes that the UI has a GtkGrid named "InnerGrid" |
| */ |
| void create_button(const char *button_text, |
| int left, |
| int top, |
| int width, |
| int height, |
| button_callback_fn button_func); |
| |
| /** |
| * Add a button convenience |
| * Adds a button at a given row index (assuming buttons in the right bar use 1 row each) |
| * by inserting a row in the grid and adding the button. Uses the default width of 3 and height of 1 |
| * |
| * @param button_text the new button text |
| * @param insert_row the row in the right bar to insert the button |
| * @param button_func callback function for the button |
| * |
| * The function assumes that the UI has a GtkGrid named "InnerGrid" |
| */ |
| void create_button(const char *button_text, int insert_row, button_callback_fn button_func); |
| |
| /** |
| * Deletes a button by its label (displayed text) |
| * |
| * @param the text of the button to delete |
| * @return whether the button was found and deleted |
| * |
| * The function assumes that the UI has a GtkGrid named "InnerGrid" |
| */ |
| bool destroy_button(const char *button_text_to_destroy); |
| |
| /** |
| * Change the label of the button (displayed text) |
| * |
| * @param button_text the old text of the button |
| * @param new_button_text the new button text |
| * |
| * The function assumes that the UI has a GtkGrid named "InnerGrid" |
| */ |
| void change_button_text(const char *button_text, const char *new_button_text); |
| |
| /** |
| * Update the message in the status bar |
| * |
| * @param message The message that will be displayed on the status bar |
| * |
| * The function assumes that the UI has a GtkStatusbar named "StatusBar" |
| */ |
| void update_message(std::string const &message); |
| |
| /** |
| * Change the coordinate system of a created canvas |
| * |
| * @param canvas_id The id of the GtkDrawingArea in the XML file. |
| * @param coordinate_system The new coordinate system of this canvas. |
| */ |
| void change_canvas_world_coordinates(std::string const &canvas_id, rectangle coordinate_system); |
| |
| /** |
| * redraw the main canvas |
| */ |
| void refresh_drawing(); |
| |
| /** |
| * Get a renderer that can be used to draw on top of the main canvas |
| */ |
| renderer *get_renderer(); |
| |
| /** |
| * Flush the drawings done by the renderer, returned from get_renderer(), to the on-screen buffer |
| * |
| * The flushing is done immediately |
| */ |
| void flush_drawing(); |
| |
| /** |
| * Run the application. |
| * |
| * Once this is called, the application will be initialized first. Initialization will build the GUI based on the XML |
| * resource given in the constructor. Once the GUI has been created, the function initial_setup_user_callback will be |
| * called. |
| * |
| * After initialization, control of the program will be given to GTK. You will only regain control for the signals |
| * that you have registered callbacks for. |
| * |
| * @param initial_setup_user_callback A user-defined function that is called before application activation |
| * @param mouse_press_user_callback The user-defined callback function for mouse press |
| * @param mouse_move_user_callback The user-defined callback function for mouse move |
| * @param key_press_user_callback The user-defined callback function for keyboard press |
| * |
| * @return The exit status. |
| */ |
| int run(setup_callback_fn initial_setup_user_callback, |
| mouse_callback_fn mouse_press_user_callback, |
| mouse_callback_fn mouse_move_user_callback, |
| key_callback_fn key_press_user_callback); |
| |
| /** |
| * Destructor. |
| */ |
| ~application(); |
| |
| /** |
| * Copies are disabled. |
| */ |
| application(application const &) = delete; |
| |
| /** |
| * Copies are disabled. |
| */ |
| application &operator=(application const &) = delete; |
| |
| /** |
| * Ownership of an application is transferrable. |
| */ |
| application(application &&) = default; |
| |
| /** |
| * Ownership of an application is transferrable. |
| */ |
| application &operator=(application &&) = default; |
| |
| /** |
| * Retrieve a pointer to a canvas that was previously added to the application. |
| * |
| * Calling this function before application::run results in undefined behaviour. |
| * |
| * @param canvas_id The key used when the canvas was added. |
| * |
| * @return A non-owning pointer, or nullptr if not found. |
| * |
| * @see application::get_object |
| */ |
| canvas *get_canvas(std::string const &canvas_id) const; |
| |
| /** |
| * Retrieve a GLib Object (i.e., a GObject). |
| * |
| * This is useful for retrieving GUI elements specified in your XML file(s). You should only call this function after |
| * the application has been run, otherwise the GUI elements will have not been created yet. |
| * |
| * @param name The ID of the object. |
| * @return The object with the ID, or NULL if it could not be found. |
| * |
| * @see application::run |
| */ |
| GObject *get_object(gchar const *name) const; |
| |
| /** |
| * Get the ID of the main window |
| */ |
| std::string get_main_window_id() const |
| { |
| return m_window_id; |
| } |
| |
| /** |
| * Get the ID of the main canvas |
| */ |
| std::string get_main_canvas_id() const |
| { |
| return m_canvas_id; |
| } |
| |
| /** |
| * Quit the application |
| */ |
| void quit(); |
| |
| private: |
| // The package path to the XML file that describes the UI. |
| std::string m_main_ui; |
| |
| // The ID of the main window to add to our GTK application. |
| std::string m_window_id; |
| |
| // The ID of the main canvas |
| std::string m_canvas_id; |
| |
| // The GTK application. |
| GtkApplication *m_application; |
| |
| // The GUI builder that parses an XML user interface. |
| GtkBuilder *m_builder; |
| |
| // The function to call when the application is starting up. |
| connect_g_objects_fn m_register_callbacks; |
| |
| // The collection of canvases added to the application. |
| std::map<std::string, std::unique_ptr<canvas>> m_canvases; |
| |
| // A flag that indicates if the run() was called before or not to allow multiple reruns |
| bool first_run; |
| |
| // A flag that indicates if we are resuming an older run to allow proper quitting |
| bool resume_run; |
| |
| private: |
| // Called when our GtkApplication is initialized for the first time. |
| static void startup(GtkApplication *gtk_app, gpointer user_data); |
| |
| // Called when GTK activates our application for the first time. |
| static void activate(GtkApplication *gtk_app, gpointer user_data); |
| |
| // Called during application activation to setup the default callbacks for the prebuilt buttons |
| static void register_default_buttons_callbacks(application *application); |
| |
| // Called during application activation to setup the default callbacks for the mouse and key events |
| static void register_default_events_callbacks(application *application); |
| |
| public: |
| // The user-defined initial setup callback function |
| setup_callback_fn initial_setup_callback; |
| |
| // The user-defined callback function for handling mouse press |
| mouse_callback_fn mouse_press_callback; |
| |
| // The user-defined callback function for handling mouse move |
| mouse_callback_fn mouse_move_callback; |
| |
| // The user-defined callback function for handling keyboard press |
| key_callback_fn key_press_callback; |
| }; |
| |
| /** |
| * Set the disable_event_loop flag to new_setting |
| * Call with new_setting == true to make the event_loop immediately return. |
| * Needed only for auto-marking |
| * |
| * @param new_setting The new state of disable_event_loop flag |
| */ |
| void set_disable_event_loop(bool new_setting); |
| } |
| |
| #endif //EZGL_APPLICATION_HPP |