399 lines
12 KiB
C++
399 lines
12 KiB
C++
/*
|
|
* 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
|