libezgl: Updating libs/EXTERNAL/libezgl/ (external git subtree from https://github.com/mariobadr/ezgl.git master)
diff --git a/libs/EXTERNAL/libezgl/CMakeLists.txt b/libs/EXTERNAL/libezgl/CMakeLists.txt index 164887e..6ab3345 100644 --- a/libs/EXTERNAL/libezgl/CMakeLists.txt +++ b/libs/EXTERNAL/libezgl/CMakeLists.txt
@@ -3,7 +3,7 @@ # create the project project( ezgl - VERSION 0.0.1 + VERSION 1.0.1 LANGUAGES CXX )
diff --git a/libs/EXTERNAL/libezgl/README.adoc b/libs/EXTERNAL/libezgl/README.adoc index da62577..836033e 100644 --- a/libs/EXTERNAL/libezgl/README.adoc +++ b/libs/EXTERNAL/libezgl/README.adoc
@@ -1,5 +1,7 @@ = EZGL - An Easy Graphics Library +image:https://codedocs.xyz/mariobadr/ezgl.svg[link="https://codedocs.xyz/mariobadr/ezgl"] + EZGL is a library for use in ece297 as a simple way to create a GUI application. The library provides a thin wrapper around GTK and drawing functionality.
diff --git a/libs/EXTERNAL/libezgl/examples/basic-application/basic_application.cpp b/libs/EXTERNAL/libezgl/examples/basic-application/basic_application.cpp index 11071b0..2b5e288 100644 --- a/libs/EXTERNAL/libezgl/examples/basic-application/basic_application.cpp +++ b/libs/EXTERNAL/libezgl/examples/basic-application/basic_application.cpp
@@ -29,7 +29,7 @@ void act_on_mouse_press(ezgl::application *application, GdkEventButton *event, double x, double y); void act_on_mouse_move(ezgl::application *application, GdkEventButton *event, double x, double y); void act_on_key_press(ezgl::application *application, GdkEventKey *event, char *key_name); -void initial_setup(ezgl::application *application); +void initial_setup(ezgl::application *application, bool new_window); void test_button(GtkWidget *widget, ezgl::application *application); /** @@ -37,19 +37,19 @@ * * The graphics object expects that x and y values will be in the main canvas' world coordinate system. */ -void draw_main_canvas(ezgl::renderer &g); +void draw_main_canvas(ezgl::renderer *g); /** * draw_main_canvas helper functions */ -void draw_rectangle_example(ezgl::renderer &g); -void draw_arc_example(ezgl::renderer &g); -void rotated_text_example(ezgl::renderer &g); -void draw_poly_example(ezgl::renderer &g); -void draw_text_example(ezgl::renderer &g); -void draw_line_example(ezgl::renderer &g); -void screen_coordinates_example(ezgl::renderer &g); -void draw_png_example(ezgl::renderer &g); +void draw_rectangle_example(ezgl::renderer *g); +void draw_arc_example(ezgl::renderer *g); +void rotated_text_example(ezgl::renderer *g); +void draw_poly_example(ezgl::renderer *g); +void draw_text_example(ezgl::renderer *g); +void draw_line_example(ezgl::renderer *g); +void screen_coordinates_example(ezgl::renderer *g); +void draw_png_example(ezgl::renderer *g); static ezgl::rectangle initial_world{{0, 0}, 1100, 1150}; @@ -99,7 +99,7 @@ /** * The redrawing function for still pictures */ -void draw_main_canvas(ezgl::renderer &g) +void draw_main_canvas(ezgl::renderer *g) { // Draw some rectangles draw_rectangle_example(g); @@ -129,7 +129,7 @@ /** * Draw some rectangles with different colors */ -void draw_rectangle_example(ezgl::renderer &g) +void draw_rectangle_example(ezgl::renderer *g) { const float rectangle_width = 50; const float rectangle_height = rectangle_width; @@ -152,25 +152,25 @@ }; // format text font and color - g.set_color(ezgl::BLACK); - g.format_font("monospace", ezgl::font_slant::normal, ezgl::font_weight::normal, 10); + g->set_color(ezgl::BLACK); + g->format_font("monospace", ezgl::font_slant::normal, ezgl::font_weight::normal, 10); // draw text - g.draw_text({110, color_rectangle.center_y()}, "colors", 2 * (start_point.x - 110), rectangle_height); + g->draw_text({110, color_rectangle.center_y()}, "colors", 2 * (start_point.x - 110), rectangle_height); for (size_t i = 0; i < sizeof (color_indicies) / sizeof (color_indicies[0]); ++i) { // Change the color of next draw calls - g.set_color(color_indicies[i]); + g->set_color(color_indicies[i]); // Draw filled in rectangles - g.fill_rectangle(color_rectangle); + g->fill_rectangle(color_rectangle); // Increment the start point color_rectangle += {rectangle_width, 0}; } // Draw text - g.draw_text({400, color_rectangle.center_y()}, "fill_rectangle", DBL_MAX, rectangle_height); + g->draw_text({400, color_rectangle.center_y()}, "fill_rectangle", DBL_MAX, rectangle_height); /* Draw some rectangles with RGB triplet colors and alpha (transparency) */ @@ -182,162 +182,162 @@ color_rectangle += {rectangle_width, 0}; // Change the next draw calls color. rgb and alpha values range from 0 to 255 - g.set_color(std::rand() % 256, std::rand() % 256, std::rand() % 256, 255); + g->set_color(std::rand() % 256, std::rand() % 256, std::rand() % 256, 255); // Draw filled in rectangles - g.fill_rectangle(color_rectangle); + g->fill_rectangle(color_rectangle); } /* Draw a black border rectangle */ // Change the next draw calls color to black - g.set_color(ezgl::BLACK); + g->set_color(ezgl::BLACK); // Change the next draw calls line width - g.set_line_width(1); + g->set_line_width(1); // Draw a rectangle bordering all the drawn rectangles - g.draw_rectangle(start_point, color_rectangle.top_right()); + g->draw_rectangle(start_point, color_rectangle.top_right()); } /** * Draw some example lines, shapes, and arcs */ -void draw_arc_example(ezgl::renderer &g) +void draw_arc_example(ezgl::renderer *g) { float radius = 50; // Draw solid line - g.set_color(ezgl::BLACK); - g.draw_text({250, 150}, "draw_line", 150.0, DBL_MAX); - g.set_line_dash(ezgl::line_dash::none); - g.draw_line({200, 120}, {200, 200}); + g->set_color(ezgl::BLACK); + g->draw_text({250, 150}, "draw_line", 150.0, DBL_MAX); + g->set_line_dash(ezgl::line_dash::none); + g->draw_line({200, 120}, {200, 200}); // Draw dashed line - g.set_line_dash(ezgl::line_dash::asymmetric_5_3); - g.draw_line({300, 120}, {300, 200}); + g->set_line_dash(ezgl::line_dash::asymmetric_5_3); + g->draw_line({300, 120}, {300, 200}); // Draw elliptic arc - g.set_color(ezgl::MAGENTA); - g.draw_text({450, 160}, "draw_elliptic_arc", 150.0, DBL_MAX); - g.draw_elliptic_arc({550, 160}, 30, 60, 90, 270); + g->set_color(ezgl::MAGENTA); + g->draw_text({450, 160}, "draw_elliptic_arc", 150.0, DBL_MAX); + g->draw_elliptic_arc({550, 160}, 30, 60, 90, 270); // Draw filled in elliptic arc - g.draw_text({700, 160}, "fill_elliptic_arc", 150.0, DBL_MAX); - g.fill_elliptic_arc({800, 160}, 30, 60, 90, 270); + g->draw_text({700, 160}, "fill_elliptic_arc", 150.0, DBL_MAX); + g->fill_elliptic_arc({800, 160}, 30, 60, 90, 270); // Draw arcs - g.set_color(ezgl::BLUE); - g.draw_text({190, 300}, "draw_arc", radius * 2, 150); - g.draw_arc({190, 300}, radius, 0, 270); - g.draw_arc({300, 300}, radius, 0, -180); + g->set_color(ezgl::BLUE); + g->draw_text({190, 300}, "draw_arc", radius * 2, 150); + g->draw_arc({190, 300}, radius, 0, 270); + g->draw_arc({300, 300}, radius, 0, -180); // Draw filled in arcs - g.fill_arc({410, 300}, radius, 90, -90); - g.fill_arc({520, 300}, radius, 0, 360); - g.set_color(ezgl::BLACK); - g.draw_text({520, 300}, "fill_arc", radius * 2, 150); - g.set_color(ezgl::BLUE); - g.fill_arc({630, 300}, radius, 90, 180); - g.fill_arc({740, 300}, radius, 90, 270); - g.fill_arc({850, 300}, radius, 90, 30); + g->fill_arc({410, 300}, radius, 90, -90); + g->fill_arc({520, 300}, radius, 0, 360); + g->set_color(ezgl::BLACK); + g->draw_text({520, 300}, "fill_arc", radius * 2, 150); + g->set_color(ezgl::BLUE); + g->fill_arc({630, 300}, radius, 90, 180); + g->fill_arc({740, 300}, radius, 90, 270); + g->fill_arc({850, 300}, radius, 90, 30); } /** * Draw some rotated text */ -void rotated_text_example(ezgl::renderer &g) +void rotated_text_example(ezgl::renderer *g) { const float textsquare_width = 200; ezgl::rectangle textsquare = {{100, 400}, textsquare_width, textsquare_width}; - g.set_color(ezgl::BLUE); - g.draw_rectangle(textsquare); + g->set_color(ezgl::BLUE); + g->draw_rectangle(textsquare); - g.set_color(ezgl::GREEN); - g.draw_rectangle(textsquare.center(), {textsquare.right(), textsquare.top()}); - g.draw_rectangle({textsquare.left(), textsquare.bottom()}, textsquare.center()); + g->set_color(ezgl::GREEN); + g->draw_rectangle(textsquare.center(), {textsquare.right(), textsquare.top()}); + g->draw_rectangle({textsquare.left(), textsquare.bottom()}, textsquare.center()); - g.set_color(ezgl::RED); - g.draw_line({textsquare.left(), textsquare.bottom()}, {textsquare.right(), textsquare.top()}); - g.draw_line({textsquare.left(), textsquare.top()}, {textsquare.right(), textsquare.bottom()}); + g->set_color(ezgl::RED); + g->draw_line({textsquare.left(), textsquare.bottom()}, {textsquare.right(), textsquare.top()}); + g->draw_line({textsquare.left(), textsquare.top()}, {textsquare.right(), textsquare.bottom()}); - g.set_color(0, 0, 0, 100); - g.set_font_size(14); - g.draw_text({textsquare.center_x(), textsquare.bottom()}, "0 degrees", textsquare.width(), textsquare.height()); + g->set_color(0, 0, 0, 100); + g->set_font_size(14); + g->draw_text({textsquare.center_x(), textsquare.bottom()}, "0 degrees", textsquare.width(), textsquare.height()); - g.set_text_rotation(90); - g.draw_text({textsquare.right(), textsquare.center_y()}, "90 degrees", textsquare.width(), textsquare.height()); + g->set_text_rotation(90); + g->draw_text({textsquare.right(), textsquare.center_y()}, "90 degrees", textsquare.width(), textsquare.height()); - g.set_text_rotation(180); - g.draw_text({textsquare.center_x(), textsquare.top()}, "180 degrees", textsquare.width(), textsquare.height()); + g->set_text_rotation(180); + g->draw_text({textsquare.center_x(), textsquare.top()}, "180 degrees", textsquare.width(), textsquare.height()); - g.set_text_rotation(270); - g.draw_text({textsquare.left(), textsquare.center_y()}, "270 degrees", textsquare.width(), textsquare.height()); + g->set_text_rotation(270); + g->draw_text({textsquare.left(), textsquare.center_y()}, "270 degrees", textsquare.width(), textsquare.height()); - g.set_text_rotation(45); - g.draw_text(textsquare.center(), "45 degrees", textsquare.width(), textsquare.height()); + g->set_text_rotation(45); + g->draw_text(textsquare.center(), "45 degrees", textsquare.width(), textsquare.height()); - g.set_text_rotation(135); - g.draw_text(textsquare.center(), "135 degrees", textsquare.width(), textsquare.height()); + g->set_text_rotation(135); + g->draw_text(textsquare.center(), "135 degrees", textsquare.width(), textsquare.height()); // It is probably a good idea to set text rotation back to zero, - g.set_text_rotation(0); + g->set_text_rotation(0); } /** * Draw some Polygons */ -void draw_poly_example(ezgl::renderer &g) +void draw_poly_example(ezgl::renderer *g) { - g.set_font_size(10); - g.set_color(ezgl::RED); + g->set_font_size(10); + g->set_color(ezgl::RED); // Draw a triangle - g.fill_poly({{500, 400}, {440, 480}, {560, 480}}); + g->fill_poly({{500, 400}, {440, 480}, {560, 480}}); // Draw a 4-point polygon - g.fill_poly({{700, 400}, {650, 480}, {750, 480}, {800, 400}}); + g->fill_poly({{700, 400}, {650, 480}, {750, 480}, {800, 400}}); - g.set_color(ezgl::BLACK); - g.draw_text({500, 450}, "fill_poly", 80.0, DBL_MAX); - g.draw_text({725, 440}, "fill_poly", 100.0, DBL_MAX); + g->set_color(ezgl::BLACK); + g->draw_text({500, 450}, "fill_poly", 80.0, DBL_MAX); + g->draw_text({725, 440}, "fill_poly", 100.0, DBL_MAX); - g.set_color(ezgl::DARK_GREEN); - g.set_line_dash(ezgl::line_dash::none); + g->set_color(ezgl::DARK_GREEN); + g->set_line_dash(ezgl::line_dash::none); ezgl::rectangle rect = {{350, 550}, {650, 670}}; - g.draw_text(rect.center(), "draw_rectangle", rect.width(), rect.height()); - g.draw_rectangle(rect); + g->draw_text(rect.center(), "draw_rectangle", rect.width(), rect.height()); + g->draw_rectangle(rect); /* Draw some semi-transparent primitives */ - g.set_font_size(10); + g->set_font_size(10); - g.set_color(255, 0, 0, 255); - g.fill_rectangle({1000, 400}, {1050, 800}); + g->set_color(255, 0, 0, 255); + g->fill_rectangle({1000, 400}, {1050, 800}); - g.set_color(0, 0, 255, 255); - g.fill_rectangle({1000+50, 400}, {1050+50, 800}); + g->set_color(0, 0, 255, 255); + g->fill_rectangle({1000+50, 400}, {1050+50, 800}); - g.set_color(0, 255, 0, 255/2); // 50% transparent - g.fill_rectangle({1000+25, 400-100}, {1050+25, 800-200}); + g->set_color(0, 255, 0, 255/2); // 50% transparent + g->fill_rectangle({1000+25, 400-100}, {1050+25, 800-200}); - g.set_color(255, 100, 255, 255/2); - g.fill_poly({{465, 380}, {400, 450}, {765, 450}, {850, 380}}); + g->set_color(255, 100, 255, 255/2); + g->fill_poly({{465, 380}, {400, 450}, {765, 450}, {850, 380}}); - g.set_color(100, 100, 255, 255/3); - g.fill_poly({{550, 420}, {475, 500}, {875, 500}}); + g->set_color(100, 100, 255, 255/3); + g->fill_poly({{550, 420}, {475, 500}, {875, 500}}); - g.set_color(ezgl::BLACK); - g.set_text_rotation(90); - g.draw_text({1000 - 50, 500}, "Partially transparent polys", 500, DBL_MAX); - g.set_text_rotation(0); + g->set_color(ezgl::BLACK); + g->set_text_rotation(90); + g->draw_text({1000 - 50, 500}, "Partially transparent polys", 500, DBL_MAX); + g->set_text_rotation(0); } /** * Draw some example text, with the bounding box functions */ -void draw_text_example(ezgl::renderer &g) +void draw_text_example(ezgl::renderer *g) { const float text_example_width = 800; @@ -362,16 +362,16 @@ {24, 32} }; - g.set_color(ezgl::BLACK); - g.set_line_dash(ezgl::line_dash::asymmetric_5_3); + g->set_color(ezgl::BLACK); + g->set_line_dash(ezgl::line_dash::asymmetric_5_3); for (int i = 0; i < num_lines; ++i) { ezgl::rectangle text_bbox = {{100., 710. + i * 60.}, text_example_width / num_strings_per_line[i], 60.}; for (int j = 0; j < num_strings_per_line[i]; ++j) { - g.set_font_size(text_sizes[i][j]); - g.draw_text(text_bbox.center(), line_text[i][j], text_bbox.width(), text_bbox.height()); - g.draw_rectangle(text_bbox); + g->set_font_size(text_sizes[i][j]); + g->draw_text(text_bbox.center(), line_text[i][j], text_bbox.width(), text_bbox.height()); + g->draw_rectangle(text_bbox); text_bbox = {{text_bbox.left() + text_example_width / num_strings_per_line[i], text_bbox.bottom()} , text_bbox.width(), text_bbox.height()}; } } @@ -380,52 +380,52 @@ /** * Draw wide lines with different end shapes */ -void draw_line_example(ezgl::renderer &g) +void draw_line_example(ezgl::renderer *g) { - g.set_font_size(10); + g->set_font_size(10); for (int i = 0; i <= 2; ++i) { double offsetY = 50*i; - g.set_horiz_text_just(ezgl::text_just::left); + g->set_horiz_text_just(ezgl::text_just::left); if (i == 0) { - g.set_color(ezgl::BLACK); - g.set_line_cap(ezgl::line_cap::butt); // Butt ends - g.set_line_dash(ezgl::line_dash::none); // Solid line - g.draw_text({950, 920+offsetY}, "Butt ends, opaque", 400, DBL_MAX); + g->set_color(ezgl::BLACK); + g->set_line_cap(ezgl::line_cap::butt); // Butt ends + g->set_line_dash(ezgl::line_dash::none); // Solid line + g->draw_text({950, 920+offsetY}, "Butt ends, opaque", 400, DBL_MAX); } else if (i == 1) { - g.set_color(ezgl::GREEN, 255*2/3); // Green line that is 33% transparent) - g.set_line_cap(ezgl::line_cap::round); // Round ends - g.set_line_dash(ezgl::line_dash::none); // Solid line - g.draw_text({950, 920+offsetY}, "Round ends, 33% transparent", 400, DBL_MAX); + g->set_color(ezgl::GREEN, 255*2/3); // Green line that is 33% transparent) + g->set_line_cap(ezgl::line_cap::round); // Round ends + g->set_line_dash(ezgl::line_dash::none); // Solid line + g->draw_text({950, 920+offsetY}, "Round ends, 33% transparent", 400, DBL_MAX); } else { - g.set_color(ezgl::RED, 255/3); // Red line that is 67% transparent - g.set_line_cap(ezgl::line_cap::butt); // butt ends - g.set_line_dash(ezgl::line_dash::asymmetric_5_3); // Dashed line - g.draw_text({950, 920+offsetY}, "Butt ends, 67% transparent", 400, DBL_MAX); + g->set_color(ezgl::RED, 255/3); // Red line that is 67% transparent + g->set_line_cap(ezgl::line_cap::butt); // butt ends + g->set_line_dash(ezgl::line_dash::asymmetric_5_3); // Dashed line + g->draw_text({950, 920+offsetY}, "Butt ends, 67% transparent", 400, DBL_MAX); } - g.set_horiz_text_just(ezgl::text_just::center); + g->set_horiz_text_just(ezgl::text_just::center); - g.draw_text({200, 900+offsetY}, "Thin line (width 1)", 200, DBL_MAX); - g.set_line_width(1); - g.draw_line({100, 920+offsetY}, {300, 920+offsetY}); + g->draw_text({200, 900+offsetY}, "Thin line (width 1)", 200, DBL_MAX); + g->set_line_width(1); + g->draw_line({100, 920+offsetY}, {300, 920+offsetY}); - g.draw_text({500, 900+offsetY}, "Width 3 Line", 200, DBL_MAX); - g.set_line_width(3); - g.draw_line({400, 920+offsetY}, {600, 920+offsetY}); + g->draw_text({500, 900+offsetY}, "Width 3 Line", 200, DBL_MAX); + g->set_line_width(3); + g->draw_line({400, 920+offsetY}, {600, 920+offsetY}); - g.draw_text({800, 900+offsetY}, "Width 6 Line", 200, DBL_MAX); - g.set_line_width(6); - g.draw_line({700, 920+offsetY}, {900, 920+offsetY}); + g->draw_text({800, 900+offsetY}, "Width 6 Line", 200, DBL_MAX); + g->set_line_width(6); + g->draw_line({700, 920+offsetY}, {900, 920+offsetY}); - g.set_line_width(1); + g->set_line_width(1); } } @@ -433,33 +433,33 @@ * Draw to screen coordinates where (0,0) is the top-left corner of the window * These coordinates are not transformed so the object will not pan or zoom. */ -void screen_coordinates_example(ezgl::renderer &g) +void screen_coordinates_example(ezgl::renderer *g) { // Set the coordinate system to SCREEN - g.set_coordinate_system(ezgl::SCREEN); + g->set_coordinate_system(ezgl::SCREEN); - g.set_color(255, 0, 0, 255); - g.set_line_dash(ezgl::line_dash::none); - g.draw_rectangle({10, 10}, {100, 100}); - g.set_font_size(10); - g.draw_text({55, 33}, "Screen coord"); - g.draw_text({55, 66}, "Fixed loc"); + g->set_color(255, 0, 0, 255); + g->set_line_dash(ezgl::line_dash::none); + g->draw_rectangle({10, 10}, {100, 100}); + g->set_font_size(10); + g->draw_text({55, 33}, "Screen coord"); + g->draw_text({55, 66}, "Fixed loc"); // Set the coordinate system back to WORLD - g.set_coordinate_system(ezgl::WORLD); + g->set_coordinate_system(ezgl::WORLD); } /** * Draw a small PNG */ -void draw_png_example(ezgl::renderer &g) +void draw_png_example(ezgl::renderer *g) { ezgl::surface *png_surface = ezgl::renderer::load_png("small_image.png"); - g.draw_surface(png_surface, {50, 200}); + g->draw_surface(png_surface, {50, 200}); ezgl::renderer::free_surface(png_surface); - g.set_font_size(10); - g.set_color(ezgl::BLACK); - g.draw_text ({50, 225}, "draw_surface", 200, DBL_MAX); + g->set_font_size(10); + g->set_color(ezgl::BLACK); + g->draw_text ({50, 225}, "draw_surface", 200, DBL_MAX); } /** @@ -467,7 +467,7 @@ * Can be used to create additional buttons, initialize the status message, * or connect added widgets to their callback functions */ -void initial_setup(ezgl::application *application) +void initial_setup(ezgl::application *application, bool /*new_window*/) { // Update the status bar message application->update_message("EZGL Application"); @@ -540,10 +540,10 @@ application->refresh_drawing(); // Draw a temporary rectangle border - ezgl::renderer g = application->get_renderer(); - g.set_line_width(1); - g.set_color(ezgl::BLACK); - g.draw_rectangle({{0, 0}, 1100, 1150}); + ezgl::renderer *g = application->get_renderer(); + g->set_line_width(1); + g->set_color(ezgl::BLACK); + g->draw_rectangle({{0, 0}, 1100, 1150}); application->flush_drawing(); }
diff --git a/libs/EXTERNAL/libezgl/include/ezgl/application.hpp b/libs/EXTERNAL/libezgl/include/ezgl/application.hpp index 256d0b0..8015f2e 100644 --- a/libs/EXTERNAL/libezgl/include/ezgl/application.hpp +++ b/libs/EXTERNAL/libezgl/include/ezgl/application.hpp
@@ -48,7 +48,7 @@ /** * The signature of a setup callback function */ -using setup_callback_fn = void (*)(application *app); +using setup_callback_fn = void (*)(application *app, bool new_window); /** * The signature of a button callback function @@ -225,16 +225,14 @@ void refresh_drawing(); /** - * Get a temporary renderer that can be used to draw on top of the main canvas - * - * The returned renderer should be used only in the same callback in which this function is called + * Get a renderer that can be used to draw on top of the main canvas */ - renderer get_renderer(); + renderer *get_renderer(); /** * Flush the drawings done by the renderer, returned from get_renderer(), to the on-screen buffer * - * The flushing is done after returning to the GTK event loop + * The flushing is done immediately */ void flush_drawing(); @@ -357,6 +355,9 @@ // 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);
diff --git a/libs/EXTERNAL/libezgl/include/ezgl/canvas.hpp b/libs/EXTERNAL/libezgl/include/ezgl/canvas.hpp index 5bf4636..b6aa475 100644 --- a/libs/EXTERNAL/libezgl/include/ezgl/canvas.hpp +++ b/libs/EXTERNAL/libezgl/include/ezgl/canvas.hpp
@@ -40,7 +40,7 @@ /** * The signature of a function that draws to an ezgl::canvas. */ -using draw_canvas_fn = void (*)(renderer &); +using draw_canvas_fn = void (*)(renderer*); /** * Responsible for creating, destroying, and maintaining the rendering context of a GtkWidget. @@ -100,11 +100,9 @@ } /** - * Create a temporary renderer that can be used to draw on top of the current canvas - * - * The created renderer should be used only in the same callback in which it was created + * Create an animation renderer that can be used to draw on top of the current canvas */ - renderer create_temporary_renderer(); + renderer *create_animation_renderer(); /** * print_pdf, print_svg, and print_png generate a PDF, SVG, or PNG output file showing @@ -158,6 +156,9 @@ // The off-screen cairo context that can be drawn to cairo_t *m_context = nullptr; + // The animation renderer + renderer *m_animation_renderer = nullptr; + private: // Called each time our drawing area widget has changed (e.g., in size). static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
diff --git a/libs/EXTERNAL/libezgl/include/ezgl/graphics.hpp b/libs/EXTERNAL/libezgl/include/ezgl/graphics.hpp index d9fd36a..35e41d4 100644 --- a/libs/EXTERNAL/libezgl/include/ezgl/graphics.hpp +++ b/libs/EXTERNAL/libezgl/include/ezgl/graphics.hpp
@@ -467,6 +467,14 @@ */ renderer(cairo_t *cairo, transform_fn transform, camera *m_camera, cairo_surface_t *m_surface); + /** + * Update the renderer when the cairo surface/context changes + * + * @param cairo The new cairo graphics state + * @param m_surface The new cairo surface + */ + void update_renderer(cairo_t *cairo, cairo_surface_t *m_surface); + private: void draw_rectangle_path(point2d start, point2d end, bool fill_flag); @@ -496,16 +504,7 @@ // The x11 context GC x11_context; - // Current line width - int current_line_width = 1; - - // Current line cap - line_cap current_line_cap = line_cap::butt; - - // Current line dash - line_dash current_line_dash = line_dash::none; - - // Transparency flag, if set cairo will be used + // Transparency flag, if set, cairo will be used bool transparency_flag = false; #endif @@ -522,6 +521,18 @@ // Current vertical text justification text_just vert_text_just = text_just::center; + + // Current line width + int current_line_width = 1; + + // Current line cap + line_cap current_line_cap = line_cap::butt; + + // Current line dash + line_dash current_line_dash = line_dash::none; + + // Current color + color current_color = {0, 0, 0, 255}; }; }
diff --git a/libs/EXTERNAL/libezgl/src/application.cpp b/libs/EXTERNAL/libezgl/src/application.cpp index b850f86..0a1cdf7 100644 --- a/libs/EXTERNAL/libezgl/src/application.cpp +++ b/libs/EXTERNAL/libezgl/src/application.cpp
@@ -63,7 +63,7 @@ } if(ezgl_app->initial_setup_callback != nullptr) - ezgl_app->initial_setup_callback(ezgl_app); + ezgl_app->initial_setup_callback(ezgl_app, true); g_info("application::activate successful."); } @@ -87,6 +87,7 @@ g_signal_connect(m_application, "activate", G_CALLBACK(activate), this); first_run = true; + resume_run = false; } application::~application() @@ -156,9 +157,35 @@ mouse_move_callback = mouse_move_user_callback; key_press_callback = key_press_user_callback; + if(first_run) { + // set the first_run flag to false + first_run = false; + + g_info("The event loop is now starting."); + + // see: https://developer.gnome.org/gio/stable/GApplication.html#g-application-run + return g_application_run(G_APPLICATION(m_application), 0, 0); + } // The result of calling g_application_run() again after it returns is unspecified. - // So we have to destruct and reconstruct the GTKApplication - if(!first_run) { + // So in the subsequent runs instead of calling g_application_run(), we will go back to the event loop using gtk_main() + else if(!first_run && gtk_application_get_active_window(m_application) != nullptr) { + + // Call user's initial setup call + if(initial_setup_callback != nullptr) + initial_setup_callback(this, false); + + // set the resume_run flag to true + resume_run = true; + + g_info("The event loop is now resuming."); + + // see: https://developer.gnome.org/gtk3/stable/gtk3-General.html#gtk-main + gtk_main(); + + return 0; + } + // But if the GTK window is closed, we will have to destruct and reconstruct the GTKApplication + else { // Destroy the GTK application g_object_unref(m_application); g_object_unref(m_builder); @@ -168,25 +195,26 @@ m_builder = (gtk_builder_new()); g_signal_connect(m_application, "startup", G_CALLBACK(startup), this); g_signal_connect(m_application, "activate", G_CALLBACK(activate), this); + + // set the resume_run flag to false + resume_run = false; + + g_info("The event loop is now restarting."); + + // see: https://developer.gnome.org/gio/stable/GApplication.html#g-application-run + return g_application_run(G_APPLICATION(m_application), 0, 0); } - - // set the first_run flag to false - first_run = false; - - g_info("The event loop is now starting."); - - // see: https://developer.gnome.org/gio/unstable/GApplication.html#g-application-run - return g_application_run(G_APPLICATION(m_application), 0, 0); } void application::quit() { - // Close the current window - GObject *window = get_object(m_window_id.c_str()); - gtk_window_close(GTK_WINDOW(window)); - - // Quit the GTK application - g_application_quit(G_APPLICATION(m_application)); + if(resume_run) { + // Quit the event loop (exit gtk_main()) + gtk_main_quit(); + } else { + // Quit the GTK application (exit g_application_run()) + g_application_quit(G_APPLICATION(m_application)); + } } void application::register_default_events_callbacks(ezgl::application *application) @@ -213,6 +241,9 @@ // Connect scroll_mouse function to the mouse scroll event (up, down, left and right) g_signal_connect(main_canvas, "scroll_event", G_CALLBACK(scroll_mouse), application); + + // Connect press_proceed function to the close button of the MainWindow + g_signal_connect(window, "destroy", G_CALLBACK(press_proceed), application); } void application::register_default_buttons_callbacks(ezgl::application *application) @@ -410,14 +441,18 @@ // queue a redraw of the GtkWidget gtk_widget_queue_draw(drawing_area); + + // run the main loop on pending events + while(gtk_events_pending()) + gtk_main_iteration(); } -renderer application::get_renderer() +renderer *application::get_renderer() { // get the main canvas canvas *cnv = get_canvas(m_canvas_id); - return cnv->create_temporary_renderer(); + return cnv->create_animation_renderer(); } void set_disable_event_loop(bool new_setting)
diff --git a/libs/EXTERNAL/libezgl/src/canvas.cpp b/libs/EXTERNAL/libezgl/src/canvas.cpp index fcf72f7..9821036 100644 --- a/libs/EXTERNAL/libezgl/src/canvas.cpp +++ b/libs/EXTERNAL/libezgl/src/canvas.cpp
@@ -86,7 +86,7 @@ camera pdf_cam = m_camera; pdf_cam.update_widget(surface_width, surface_height); renderer g(context, std::bind(&camera::world_to_screen, pdf_cam, _1), &pdf_cam, pdf_surface); - m_draw_callback(g); + m_draw_callback(&g); // free surface & context cairo_surface_destroy(pdf_surface); @@ -125,7 +125,7 @@ camera svg_cam = m_camera; svg_cam.update_widget(surface_width, surface_height); renderer g(context, std::bind(&camera::world_to_screen, svg_cam, _1), &svg_cam, svg_surface); - m_draw_callback(g); + m_draw_callback(&g); // free surface & context cairo_surface_destroy(svg_surface); @@ -164,7 +164,7 @@ camera png_cam = m_camera; png_cam.update_widget(surface_width, surface_height); renderer g(context, std::bind(&camera::world_to_screen, png_cam, _1), &png_cam, png_surface); - m_draw_callback(g); + m_draw_callback(&g); // create png output file cairo_surface_write_to_png(png_surface, file_name); @@ -205,6 +205,10 @@ // Draw to the newly created surface. ezgl_canvas->redraw(); + // Update the animation renderer + if(ezgl_canvas->m_animation_renderer != nullptr) + ezgl_canvas->m_animation_renderer->update_renderer(p_context, p_surface); + g_info("canvas::configure_event has been handled."); return TRUE; // the configure event was handled } @@ -241,6 +245,10 @@ if(m_context != nullptr) { cairo_destroy(m_context); } + + if(m_animation_renderer != nullptr) { + delete m_animation_renderer; + } } int canvas::width() const @@ -287,19 +295,21 @@ cairo_paint(m_context); using namespace std::placeholders; - renderer g(m_context, std::bind(&camera::world_to_screen, m_camera, _1), &m_camera, m_surface); - m_draw_callback(g); + renderer g(m_context, std::bind(&camera::world_to_screen, &m_camera, _1), &m_camera, m_surface); + m_draw_callback(&g); gtk_widget_queue_draw(m_drawing_area); g_info("The canvas will be redrawn."); } -renderer canvas::create_temporary_renderer() +renderer *canvas::create_animation_renderer() { - using namespace std::placeholders; - renderer g(m_context, std::bind(&camera::world_to_screen, m_camera, _1), &m_camera, m_surface); + if(m_animation_renderer == nullptr) { + using namespace std::placeholders; + m_animation_renderer = new renderer(m_context, std::bind(&camera::world_to_screen, &m_camera, _1), &m_camera, m_surface); + } - return g; + return m_animation_renderer; } } // namespace ezgl
diff --git a/libs/EXTERNAL/libezgl/src/graphics.cpp b/libs/EXTERNAL/libezgl/src/graphics.cpp index b7574c4..28ce32f 100644 --- a/libs/EXTERNAL/libezgl/src/graphics.cpp +++ b/libs/EXTERNAL/libezgl/src/graphics.cpp
@@ -56,6 +56,36 @@ #endif } +void renderer::update_renderer(cairo_t *cairo, cairo_surface_t *m_surface) +{ + // Update Cairo Context + m_cairo = cairo; + + // Update X11 Context +#ifdef EZGL_USE_X11 + // Check if the created cairo surface is an XLIB surface + if (cairo_surface_get_type(m_surface) == CAIRO_SURFACE_TYPE_XLIB) { + // get the underlying x11 drawable used by cairo surface + x11_drawable = cairo_xlib_surface_get_drawable(m_surface); + + // get the x11 display + x11_display = cairo_xlib_surface_get_display(m_surface); + + // create the x11 context from the drawable of the cairo surface + if (x11_display != nullptr) { + XFreeGC(x11_display, x11_context); + x11_context = XCreateGC(x11_display, x11_drawable, 0, 0); + } + } +#endif + + // Restore graphics attributes + set_color(current_color); + set_line_width(current_line_width); + set_line_cap(current_line_cap); + set_line_dash(current_line_dash); +} + void renderer::set_coordinate_system(t_coordinate_system new_coordinate_system) { current_coordinate_system = new_coordinate_system; @@ -161,6 +191,9 @@ // set color for cairo cairo_set_source_rgba(m_cairo, red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0); + // set current_color + current_color = {red, green, blue, alpha}; + #ifdef EZGL_USE_X11 // check transparency if(alpha != 255) @@ -185,9 +218,10 @@ auto cairo_cap = static_cast<cairo_line_cap_t>(cap); cairo_set_line_cap(m_cairo, cairo_cap); + current_line_cap = cap; + #ifdef EZGL_USE_X11 if (x11_display != nullptr) { - current_line_cap = cap; XSetLineAttributes(x11_display, x11_context, current_line_width, current_line_dash == line_dash::none ? LineSolid : LineOnOffDash, current_line_cap == line_cap::butt ? CapButt : CapRound, JoinMiter); @@ -208,9 +242,10 @@ cairo_set_dash(m_cairo, dashes, num_dashes, 0); } + current_line_dash = dash; + #ifdef EZGL_USE_X11 if (x11_display != nullptr) { - current_line_dash = dash; XSetLineAttributes(x11_display, x11_context, current_line_width, current_line_dash == line_dash::none ? LineSolid : LineOnOffDash, current_line_cap == line_cap::butt ? CapButt : CapRound, JoinMiter); @@ -222,9 +257,10 @@ { cairo_set_line_width(m_cairo, width == 0 ? 1 : width); + current_line_width = width; + #ifdef EZGL_USE_X11 if (x11_display != nullptr) { - current_line_width = width; XSetLineAttributes(x11_display, x11_context, current_line_width, current_line_dash == line_dash::none ? LineSolid : LineOnOffDash, current_line_cap == line_cap::butt ? CapButt : CapRound, JoinMiter);