diff --git a/IBMPlexSans-Regular.ttf b/IBMPlexSans-Regular.ttf deleted file mode 100644 index b581964..0000000 Binary files a/IBMPlexSans-Regular.ttf and /dev/null differ diff --git a/Menu.cpp b/Menu.cpp deleted file mode 100644 index 41b8b0f..0000000 --- a/Menu.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "header-files/Menu.h" - -Menu::Menu(EditModeMouseFunction& e, InsertModeMouseFunction& i) : editModeMouseFunction(e), insertModeMouseFunction(i) { - for (int i = 0; i < 20; i++) { - currentHover.push_back(false); - } -} - -Menu::~Menu() { - al_destroy_bitmap(divider); - al_destroy_bitmap(background); - al_destroy_font(font); - for (auto i : buttons) { - al_destroy_bitmap(i); - } - for (auto i : hoverButtons) { - al_destroy_bitmap(i); - } - for (auto i : colorButtons) { - al_destroy_bitmap(i); - } - for (auto i : colorHoverButtons) { - al_destroy_bitmap(i); - } -} - -void Menu::initDivider(ALLEGRO_BITMAP* div) { - divider = div; -} - -void Menu::initBackground(ALLEGRO_BITMAP* bg) { - background = bg; -} - -void Menu::initButtons(vector bts) { - buttons = bts; -} - -void Menu::initHoverButtons(vector bts) { - hoverButtons = bts; -} - -void Menu::initColorButtons(vector bts) { - colorButtons = bts; -} - -void Menu::initColorHoverButtons(vector bts) { - colorHoverButtons = bts; -} - -void Menu::initFont(ALLEGRO_FONT* f) { - font = f; -} - -void Menu::timer(int mode) { - if (!mode) { - editModeMouseFunction.timer(); - } - else if (mode == 1) { - insertModeMouseFunction.timer(); - } - - // draw background - al_draw_bitmap(background, 0, 0, 0); - - // draw both dividers - al_draw_bitmap(divider, 0, 75, 0); - al_draw_bitmap(divider, 0, 103, 0); - - // draw buttons before color buttons - // first 10 buttons follow this sizing scheme - for (unsigned int i = 0; i < 10; i++) { - al_draw_bitmap(buttons.at(i), i*75, 0, 0); - } - - // draw color buttons - for (unsigned int i = 0; i < 4; i++) { - al_draw_bitmap(colorButtons.at(i), 10*75 + 37*i, 0, 0); - } - for (unsigned int i = 4; i < 8; i++) { - al_draw_bitmap(colorButtons.at(i), 10*75 + 37*(i-4), 38, 0); - } - // draw save button - al_draw_bitmap(buttons.at(10), 900, 14, 0); - - // draw help button - al_draw_bitmap(buttons.at(11), 950, 14, 0); -} - -void Menu::detectMouse(int px, int py) { - // first 10 buttons follow this sizing scheme - for (unsigned int i = 0; i < 10; i++) { - if (py < 75 && py > 0 && px >= i*75 && px < (i+1)*75) { - al_draw_bitmap(hoverButtons[i], i*75, 0, 0); - al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i]); - currentHover.at(i) = true; - } - else { - currentHover.at(i) = false; - } - } - for (unsigned int i = 0; i < 4; i++) { - if (py < 37 && py > 0 && px >= 10 * 75 + 37 * i && px < 10 * 75 + 37 * (i + 1)) { - al_draw_bitmap(colorHoverButtons.at(i), 10 * 75 + 37 * i, 0, 0); - al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i+10]); - currentHover.at(i + 10) = true; - } - else { - currentHover.at(i + 10) = false; - } - } - for (unsigned int i = 4; i < 8; i++) { - if (py < 75 && py > 37 && px >= 10 * 75 + 37 * (i - 4) && px < 10 * 75 + 37 * (i - 4 + 1)) { - al_draw_bitmap(colorHoverButtons.at(i), 10 * 75 + 37 * (i - 4), 38, 0); - al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i-4+14]); - currentHover.at(i + 10) = true; - } - else { - currentHover.at(i + 10) = false; - } - } - - if (py > 14 && py < 69 && px >= 900 && px < 950) { - al_draw_bitmap(hoverButtons.at(10), 900, 14, 0); - al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[18]); - currentHover.at(18) = true; - } - else { - currentHover.at(18) = false; - } - if (py > 14 && py < 69 && px >= 950 && px < 1000) { - al_draw_bitmap(hoverButtons.at(11), 950, 14, 0); - al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[19]); - currentHover.at(19) = true; - } - else { - currentHover.at(19) = false; - } -} - -bool Menu::checkOverButton(int x) { - return currentHover.at(x); -} diff --git a/Menu.h b/Menu.h deleted file mode 100644 index d368551..0000000 --- a/Menu.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MENU_H -#define MENU_H - -#include "ECGraphicViewImp.h" - -#include -#include -#include -#include -#include - -using namespace std; - -class Menu { -public: - Menu() {} - ~Menu() {} // TODO: destory all bitmaps - void initBackrgound(ALLEGRO_BITMAP* bg); - void initButtons(vector bts); - void initHoverButtons(vector bts); - void initFont(ALLEGRO_FONT* f); - void draw(); // draws all menu buttons - void detectMouse(int px, int py); -private: - ALLEGRO_BITMAP* background; - ALLEGRO_FONT* font; - vector buttons; - vector hoverButtons; - vector hoverStrings = { - "Edit mode", - "Insert mode", - "Undo", - "Redo", - "Delete", - "Group", - "Insert rectangle", - "Insert ellipse", - "Insert filled rectangle", - "Insert filled ellipse" - }; -}; - -#endif diff --git a/README.md b/README.md index b2e6f3d..5a7b87c 100644 --- a/README.md +++ b/README.md @@ -3,28 +3,29 @@ This is a Graphical Editor written in C++ using the game engine Allegro as the basis for the GUI. ### To Compile: -g++ source-files/ECGraphicViewImp.cpp source-files/test-gv.cpp source-files/Command.cpp source-files/ShapesModel.cpp source-files/Shape.cpp source-files/ECRealObserver.cpp source-files/MouseFunction.cpp source-file/Menu.cpp -lallegro -lallegro_font -lallegro_primitives -lallegro_main -lallegro_image -lallegro_main -o editor\ -**Note:** Allegro must be installed. +g++ source-files/ECGraphicViewImp.cpp source-files/GraphicViewMain.cpp source-files/Command.cpp source-files/ShapesModel.cpp source-files/Shape.cpp source-files/ECRealObserver.cpp source-files/MouseFunction.cpp source-files/Menu.cpp -lallegro -lallegro_font -lallegro_primitives -lallegro_main -lallegro_image -lallegro_main -lallegro_dialog -lallegro_ttf -o editor + +**Note:** Allegro must be installed. If you are on Mac or Linux, Allegro can be installed directly. If you are on Windows, you can run Allegro5 applications through visual studio. Please refer to [this video](https://www.youtube.com/watch?v=UgGKBW_kU20) for instructions. ### To Run: -./editor [filename.dat/.txt]\ +./editor [\.dat/.txt]\ The above filename is optional. If included, it will attempt to load from the file. When the editor is closed, it will save to the filename that was provided. If no file is provided, the editor can still be run, but it will be blank upon opening and will not save once closed. ### Edit Mode: This is the default mode when the editor is opened.\ -**To switch modes, press the spacebar.**\ +**To switch modes, press the spacebar or click on the respective mode button in the menu.**\ In this mode, the user can select and manipulate shapes.\ **Selecting:** To select a shape, simply click within the shape's area. If there are multiple shapes in the same area, the one added most recently is selected. The selected shape will show up in blue.\ **Deselecting**: To deselect a shape, click outside any shapes.\ -**Deleting:** To delete a shape, select it and then press the "D" key.\ +**Deleting:** To delete a shape, select it and then press the "D" key or press the delete button in the menu.\ **Moving:** To move a shape, select it, and while the mouse is pressed, move the mouse to a different location. Note that when moving a shape, a light blue trace shape follows the user's mouse. It will become a real shape once the user releases the mouse.\ **Multiple Select:** By holding ctrl, multiple shapes can be selected at the same time. As with individual shapes, clicking outside of any shape will unselect all shapes. All selected shapes will be colored blue. When multiple (or individual) shapes are selected, any amount of shapes can be moved by holding the mouse down and dragging the shape to a new location, or using the arrow keys to move the selected shapes.\ -**Grouping:** When multiple shapes are selected, by pressing G, the shapes will become grouped to become a composite shape. Note that recursive grouping is possible (i.e., a composite shape can be a member of another composite shape, etc.)\ -**Ungrouping:** If only a single shape is selected, if it is a composite shape, pressing G will ungroup the shape. There +**Grouping:** When multiple shapes are selected, by pressing G or by pressing the group button in the menu, the shapes will become grouped to become a composite shape. Note that recursive grouping is possible (i.e., a composite shape can be a member of another composite shape, etc.)\ +**Ungrouping:** If only a single shape is selected, if it is a composite shape, pressing G or pressing the group button in menu will ungroup the shape. There can be multiple levels of composition. ### Insert Mode: @@ -35,10 +36,12 @@ There are four types of shapes that can be inserted: * Unfilled Ellipse * Filled Ellipse -The Unfilled Rectangle is the default shape. Pressing the "F" key once allows a filled shape to be pressed, and pressing it again goes back to unfilled mode. Pressing "G" once switches to inserting Ellipses. Pressing "G" again goes back to inserting Rectangles. +The Unfilled Rectangle is the default shape. Pressing the "F" key once allows a filled shape to be pressed, and pressing it again goes back to unfilled mode. Pressing "G" once switches to inserting Ellipses. Pressing "G" again goes back to inserting Rectangles. Also, by pressing the corresponding shape buttons in menu, the insert shape will be changed. + +The color of the insert shape can be changed by clicking the color buttons in the menu. ### Undo/Redo: -The following actions can be undone/redone using the "Z" key for Undo and the "Y" key for redo: +The following actions can be undone/redone using the "Z" key for Undo and the "Y" key for redo or the corresponding undo and redo buttons in the menu: * Inserting a shape * Deleting a shape * Moving a shape @@ -48,15 +51,35 @@ The following actions can be undone/redone using the "Z" key for Undo and the "Y Note that whenever a new action is completed (inserting, moving, etc.), all pending Redo operations are cleared. This is common convention for graphical editors using the command pattern. ### File I/O: -By running the executable with a file as the first argument, the editor will attempt to -load from the file, putting all of the shapes in the model and on screen if the file can be parsed. -After the editor is closed, every shape in the model will be saved to the file, and can be loaded -at a later time. The file will be created if it does not exist. +The editor can be used to save and load graphical images. The editor uses a file scheme that saves all information about the shapes of a graphical image into a text file in the format of .txt or .dat. + +The file scheme is as follows: +* The first line of every document is the number of shapes. +* Every line that follows gives information on an individual shape. +* The first number in each line specifies the type of shape. + * 0 - Unfilled Rectangle + * 1 - Unfilled Ellipse + * 2 - Filled Rectangle + * 3 - Filled Ellipse + * 4 - Composite Shape +* Rectangles (both unfilled and filled): the second number is the number of sides and the next eight numbers after that are (x, y) coordinate pairs of the corners of the shape. +* Ellipses (both unfilled and filled): the following two numbers are the (x, y) coordinates of the center point, with the next two following numbers are the x radius and y radius of the ellipse. +* Composite shape: this is what a shape is called if it has been grouped. There are only two numbers on this line, the first specifying that the shape is composite, and the next number *n* being the number of shapes in the composite shape. **Note: within the *n* shapes contained within each composite shape there can be nested composite shapes.** +* The last number of every shape (every atomic shape, so excluding composite shapes) specifies the color of the shape. + +By running the executable with a file name as the first argument, the editor will attempt to load from the file, putting all of the shapes in the model and on screen if the file can be parsed. If the file does not exist in the specified directory, an empty file will be created with the given name. The editor will appear blank apart from the menu. + +After the editor is closed, every shape in the model will be saved to the file, and can be loaded at a later time. + +### Other Important Features +Users can save to the document they specified on the command line by pressing the S key or by clicking the save button in the menu. + +Users can get help information in-application by pressing the H key or by clicking the help button in the menu. ### Future Features: -* Documentation on shape scheme +* ~~Documentation on shape scheme~~ *(Complete)* +* ~~Menu onscreen allowing users to change colors of shapes, changes modes, etc.~~ *(Complete)* * Makefile for easier compilation -* Menu onscreen allowing users to change colors of shapes, changes modes, etc. *(in progress)* * Add support for users to add onscreen text * Allowing users to specify which file to open in the application instead of only via command line * Detailed documentation on design patterns used @@ -64,4 +87,12 @@ at a later time. The file will be created if it does not exist. ### Copyright Info -© Tyler Hinrichs 2022 \ No newline at end of file +This project was developed as an extension of the final project for CSE 3150 at UConn in the Fall 2022 semester taught by [Dr. Yufeng Wu](https://yufeng-wu.uconn.edu/). + +Prof. Wu developed some of the starter code for this project, as well as the general guidelines (including the shape scheme). Please refer to the [first commit](https://github.com/tylernh10/graphical-editor/commit/a6a3f97ce2edbd855a279af5db7a5f096f67467b) of this repository to see the starter code he developed. This mainly included Allegro5 initialization. All other code is original. + +Version 2.0.0 includes a fully funtional menu, the ability to save the document (other than on application close), the addition of a help dialog, the ability to change the color of shape inserted, as well as general improvements to the code as well as improved organization and documentation. Note that I have written all documentation in this file (README.md). + +All icons provided by [Icons8](https://icons8.com/). + +© Tyler Hinrichs 2022 diff --git a/documentation/v2.0.0-demo.mp4 b/documentation/v2.0.0-demo.mp4 new file mode 100644 index 0000000..d9438f6 Binary files /dev/null and b/documentation/v2.0.0-demo.mp4 differ diff --git a/editor b/editor index 5c6dc9a..e744a89 100755 Binary files a/editor and b/editor differ diff --git a/header-files/Command.h b/header-files/Command.h index 92b3876..b65e82b 100644 --- a/header-files/Command.h +++ b/header-files/Command.h @@ -33,7 +33,7 @@ class InsertShape : public ECCommand { // deletes all shapes passed in class DeleteShape : public ECCommand { public: - DeleteShape(vector& s, ShapesModel* model): s(s), ECCommand(model) {} + DeleteShape(vector& s, ShapesModel* model): ECCommand(model), s(s) {} virtual ~DeleteShape() {} virtual void Execute() override; virtual void UnExecute() override; @@ -44,7 +44,7 @@ class DeleteShape : public ECCommand { // moves all shapes passed in class MoveShape : public ECCommand { public: - MoveShape(int translateX, int translateY, vector& s, ShapesModel * model): translateX(translateX), translateY(translateY), s(s), ECCommand(model) {} + MoveShape(int translateX, int translateY, vector& s, ShapesModel * model): ECCommand(model), translateX(translateX), translateY(translateY), s(s) {} virtual ~MoveShape() {} virtual void Execute() override; virtual void UnExecute() override; @@ -56,7 +56,7 @@ class MoveShape : public ECCommand { class Group : public ECCommand { public: - Group(vector& s, ShapesModel* model); + Group(vector s, ShapesModel* model); virtual ~Group() {} virtual void Execute() override; virtual void UnExecute() override; @@ -67,7 +67,7 @@ class Group : public ECCommand { class Ungroup : public ECCommand { public: - Ungroup(CompositeShape* c, ShapesModel* model): c(c), s(c->getShapes()), ECCommand(model) {} + Ungroup(CompositeShape* c, ShapesModel* model): ECCommand(model), s(c->getShapes()), c(c) {} virtual ~Ungroup() {} virtual void Execute() override; virtual void UnExecute() override; diff --git a/header-files/ECGraphicViewImp.h b/header-files/ECGraphicViewImp.h index 05bdd9d..4824227 100644 --- a/header-files/ECGraphicViewImp.h +++ b/header-files/ECGraphicViewImp.h @@ -86,10 +86,10 @@ extern ALLEGRO_COLOR arrayAllegroColors[ECGV_NUM_COLORS]; //*********************************************************** // Drawing context (thickness and so on) -class ECDrawiingContext +class ECDrawingContext { public: - ECDrawiingContext() : thickness(3), color(ECGV_BLACK) {} + ECDrawingContext() : thickness(3), color(ECGV_BLACK) {} void SetThickness(int t) { thickness = t; } int GetThickness() const { return thickness; } void SetColor(ECGVColor c) { color = c; } diff --git a/header-files/Menu.h b/header-files/Menu.h index 2337515..68c0553 100644 --- a/header-files/Menu.h +++ b/header-files/Menu.h @@ -36,7 +36,7 @@ class Menu { vector hoverButtons; vector colorButtons; vector colorHoverButtons; - vector hoverStrings = { + vector hoverStrings = { "Switch to edit mode", "Switch to insert mode", "Undo", diff --git a/header-files/Shape.h b/header-files/Shape.h index 52c970e..f4d30cc 100644 --- a/header-files/Shape.h +++ b/header-files/Shape.h @@ -12,8 +12,8 @@ class Shape { Shape(int x1, int y1, int x2, int y2, ECGVColor color = ECGV_BLACK, int type=0): x1(x1), y1(y1), x2(x2), y2(y2), color(color), type(type) { attributes.SetColor(color); } - ~Shape() {} - ECDrawiingContext getAttributes() const { return attributes; } + virtual ~Shape() {} + ECDrawingContext getAttributes() const { return attributes; } int getX1() const { return x1; } int getY1() const { return y1; } int getX2() const { return x2; } @@ -29,7 +29,7 @@ class Shape { return color; } private: - ECDrawiingContext attributes; + ECDrawingContext attributes; int x1; // corner 1 x value int y1; // corner 1 y value int x2; // corner 2 x value @@ -40,7 +40,7 @@ class Shape { class Rectangle : public Shape { public: - Rectangle(int x1, int y1, int x2, int y2, ECGVColor color = ECGV_BLACK, int type=0): Shape(x1, y1, x2, y2, color, 0) {} + Rectangle(int x1, int y1, int x2, int y2, ECGVColor color = ECGV_BLACK, int type=0): Shape(x1, y1, x2, y2, color, type) {} virtual bool isPointInside(int px, int py) const override; virtual void Draw(ECGraphicViewImp& view) const override; virtual void writeShape(ofstream& f) const override; diff --git a/source-files/Command.cpp b/source-files/Command.cpp index 78bbfd2..faf35b1 100644 --- a/source-files/Command.cpp +++ b/source-files/Command.cpp @@ -2,7 +2,7 @@ // insert command InsertShape :: InsertShape(int x1, int y1, int x2, int y2, ShapesModel* model, int type) : ECCommand(model) { - // inserts a Rectangle by default, if type is asserted, inserts an Ellipse + // inserts a Rectangle by default (type 0) if (type == 0) s = new Rectangle(x1, y1, x2, y2, model->getColor()); else if (type == 1) s = new Ellipse(x1, y1, x2, y2, model->getColor()); else if (type == 2) s = new FilledRectangle(x1, y1, x2, y2, model->getColor()); @@ -45,7 +45,7 @@ void MoveShape::UnExecute() { } // group command -Group::Group(vector& s, ShapesModel* model) : s(s), ECCommand(model) { +Group::Group(vector s, ShapesModel* model) : ECCommand(model), s(s) { c = new CompositeShape(s); } diff --git a/source-files/ECGraphicViewImp.cpp b/source-files/ECGraphicViewImp.cpp index d2015ec..bdbdde6 100644 --- a/source-files/ECGraphicViewImp.cpp +++ b/source-files/ECGraphicViewImp.cpp @@ -4,6 +4,8 @@ // // Created by Yufeng Wu on 3/2/22. // +// Modified by Tyler Hinrichs in 2022-2023. +// #include "../header-files/ECGraphicViewImp.h" #include "allegro5/allegro_primitives.h" @@ -33,7 +35,7 @@ ALLEGRO_COLOR arrayAllegroColors[ECGV_NUM_COLORS] = // A graphic view implementation // This is built on top of the Allegro library -ECGraphicViewImp::ECGraphicViewImp(int width, int height) : widthView(width), heightView(height), fRedraw(false), display(NULL), timer(NULL), event_queue(NULL) +ECGraphicViewImp::ECGraphicViewImp(int width, int height) : widthView(width), heightView(height), fRedraw(false), display(NULL), event_queue(NULL), timer(NULL) { Init(); } @@ -176,7 +178,6 @@ void ECGraphicViewImp::Shutdown() al_destroy_event_queue(event_queue); event_queue = NULL; } - cout << "all allegro components destroyed" << endl; } ECGVEventType ECGraphicViewImp::WaitForEvent() diff --git a/source-files/ECRealObserver.cpp b/source-files/ECRealObserver.cpp index a38ae62..cb58a35 100644 --- a/source-files/ECRealObserver.cpp +++ b/source-files/ECRealObserver.cpp @@ -5,8 +5,8 @@ void ECModeObserver :: Update() { // using spacebar to switch modes if (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_SPACE || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(0) && ctrl.getMode() == 1 || // switching to edit with buttons - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(1) && ctrl.getMode() == 0) { // switching to insert with buttons + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(0) && ctrl.getMode() == 1) || // switching to edit with buttons + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(1) && ctrl.getMode() == 0)) { // switching to insert with buttons ctrl.changeMode(); if (ctrl.getMode() == 0) view.defaultCursor(); if (ctrl.getMode() == 1) view.insertCursor(); @@ -32,8 +32,8 @@ void ECDrawObserver :: Update() { // ECDelObserver void ECDelObserver :: Update() { - if (ctrl.getMode() == 0 && view.GetCurrEvent() == ECGV_EV_KEY_DOWN_D || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(4)) { + if (ctrl.getMode() == 0 && + (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_D || (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(4)))) { ctrl.deleteShape(); // deletes selected shape if in edit mode view.SetRedraw(true); } @@ -55,11 +55,11 @@ void ECMouseObserver::Update() { // ECUndoRedoObserver void ECUndoRedoObserver :: Update() { if (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_Z || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(2)) { + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(2))) { ctrl.Undo(); } if (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_Y || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(3)) { + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(3))) { ctrl.Redo(); } view.SetRedraw(true); // correct drawing for last undo @@ -69,8 +69,8 @@ void ECUndoRedoObserver :: Update() { void ECGroupObserver::Update() { // must be in edit mode, then check whether G is pressed or pressing the group button in menu if (ctrl.getMode() == 0 && - view.GetCurrEvent() == ECGV_EV_KEY_DOWN_G || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(5)) { + (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_G || + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(5)))) { ctrl.GroupShapes(); ctrl.removeSelected(); } @@ -124,20 +124,20 @@ void ECColorObserver::Update() { void ECSaveObserver::Update() { if (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_S || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(18)) { + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(18))) { ctrl.save(); } } void ECHelpObserver::Update() { if (view.GetCurrEvent() == ECGV_EV_KEY_DOWN_H || - view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(19)) { + (view.GetCurrEvent() == ECGV_EV_MOUSE_BUTTON_DOWN && menu.checkOverButton(19))) { al_show_native_message_box(view.getDisplay(), "Help", "Graphical Editor Help Information", "Use the mouse to draw, select, and move shapes.\n" "Hover over a button in the menu at the top of the screen to see what it does.\n\n" "The keyboard can also be used for menu operations.\n" "Press the spacebar to switch modes (edit and insert).\n" - "Press S to save.\n" + "Press S to save. (Note that the graphical image will be automatically saved to the file specified on the command line when the application closes)\n" "Press H to open the Help Dialog (this message).\n" "Press Z to undo an action.\n" "Press Y to redo an action.\n\n" diff --git a/source-files/test-gv.cpp b/source-files/GraphicViewMain.cpp similarity index 98% rename from source-files/test-gv.cpp rename to source-files/GraphicViewMain.cpp index 313da8e..3a1d60d 100644 --- a/source-files/test-gv.cpp +++ b/source-files/GraphicViewMain.cpp @@ -1,6 +1,3 @@ -// -// Created by Yufeng Wu on 3/2/22. -// #include "../header-files/ECGraphicViewImp.h" #include "../header-files/ECRealObserver.h" #include "../header-files/ShapesModel.h" @@ -12,7 +9,7 @@ using namespace std; CompositeShape* parseComposite(int numMembers, ifstream& f, ShapesModel* model) { vector shapes; - while (shapes.size() < numMembers) { + while (shapes.size() < (long unsigned int)numMembers) { vector x; string data; getline(f, data); @@ -52,7 +49,7 @@ int real_main(int argc, char** argv) // menu init Menu menu(editMouseFunctionality, insertMouseFunctionality); - menu.initFont(al_load_ttf_font("IBMPlexSans-Regular.ttf", 18, 0)); + menu.initFont(al_load_ttf_font("res/IBMPlexSans-Regular.ttf", 18, 0)); menu.initDivider(al_load_bitmap("res/divider.jpg")); menu.initBackground(al_load_bitmap("res/background.jpg")); diff --git a/source-files/Menu.cpp b/source-files/Menu.cpp index 0dbcdd8..f6f271f 100644 --- a/source-files/Menu.cpp +++ b/source-files/Menu.cpp @@ -1,9 +1,143 @@ -#include "header-files/Menu.h" +#include "../header-files/Menu.h" -void Menu::initBackrgound(ALLEGRO_BITMAP* bg) { +Menu::Menu(EditModeMouseFunction& e, InsertModeMouseFunction& i) : editModeMouseFunction(e), insertModeMouseFunction(i) { + for (int i = 0; i < 20; i++) { + currentHover.push_back(false); + } +} + +Menu::~Menu() { + al_destroy_bitmap(divider); + al_destroy_bitmap(background); + al_destroy_font(font); + for (auto i : buttons) { + al_destroy_bitmap(i); + } + for (auto i : hoverButtons) { + al_destroy_bitmap(i); + } + for (auto i : colorButtons) { + al_destroy_bitmap(i); + } + for (auto i : colorHoverButtons) { + al_destroy_bitmap(i); + } +} + +void Menu::initDivider(ALLEGRO_BITMAP* div) { + divider = div; +} + +void Menu::initBackground(ALLEGRO_BITMAP* bg) { background = bg; } -void Menu::draw() { +void Menu::initButtons(vector bts) { + buttons = bts; +} + +void Menu::initHoverButtons(vector bts) { + hoverButtons = bts; +} + +void Menu::initColorButtons(vector bts) { + colorButtons = bts; +} + +void Menu::initColorHoverButtons(vector bts) { + colorHoverButtons = bts; +} + +void Menu::initFont(ALLEGRO_FONT* f) { + font = f; +} + +void Menu::timer(int mode) { + if (!mode) { + editModeMouseFunction.timer(); + } + else if (mode == 1) { + insertModeMouseFunction.timer(); + } + + // draw background al_draw_bitmap(background, 0, 0, 0); + + // draw both dividers + al_draw_bitmap(divider, 0, 75, 0); + al_draw_bitmap(divider, 0, 103, 0); + + // draw buttons before color buttons + // first 10 buttons follow this sizing scheme + for (unsigned int i = 0; i < 10; i++) { + al_draw_bitmap(buttons.at(i), i*75, 0, 0); + } + + // draw color buttons + for (unsigned int i = 0; i < 4; i++) { + al_draw_bitmap(colorButtons.at(i), 10*75 + 37*i, 0, 0); + } + for (unsigned int i = 4; i < 8; i++) { + al_draw_bitmap(colorButtons.at(i), 10*75 + 37*(i-4), 38, 0); + } + // draw save button + al_draw_bitmap(buttons.at(10), 900, 14, 0); + + // draw help button + al_draw_bitmap(buttons.at(11), 950, 14, 0); +} + +void Menu::detectMouse(int px, int py) { + // first 10 buttons follow this sizing scheme + for (int i = 0; i < 10; i++) { + if (py < 75 && py > 0 && px >= i*75 && px < (i+1)*75) { + al_draw_bitmap(hoverButtons[i], i*75, 0, 0); + al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i]); + currentHover.at(i) = true; + } + else { + currentHover.at(i) = false; + } + } + for (int i = 0; i < 4; i++) { + if (py < 37 && py > 0 && px >= 10 * 75 + 37 * i && px < 10 * 75 + 37 * (i + 1)) { + al_draw_bitmap(colorHoverButtons.at(i), 10 * 75 + 37 * i, 0, 0); + al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i+10]); + currentHover.at(i + 10) = true; + } + else { + currentHover.at(i + 10) = false; + } + } + for (int i = 4; i < 8; i++) { + if (py < 75 && py > 37 && px >= 10 * 75 + 37 * (i - 4) && px < 10 * 75 + 37 * (i - 4 + 1)) { + al_draw_bitmap(colorHoverButtons.at(i), 10 * 75 + 37 * (i - 4), 38, 0); + al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[i-4+14]); + currentHover.at(i + 10) = true; + } + else { + currentHover.at(i + 10) = false; + } + } + + if (py > 14 && py < 69 && px >= 900 && px < 950) { + al_draw_bitmap(hoverButtons.at(10), 900, 14, 0); + al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[18]); + currentHover.at(18) = true; + } + else { + currentHover.at(18) = false; + } + if (py > 14 && py < 69 && px >= 950 && px < 1000) { + al_draw_bitmap(hoverButtons.at(11), 950, 14, 0); + al_draw_text(font, al_map_rgb(0, 0, 0), 3, 80, ALLEGRO_ALIGN_LEFT, hoverStrings[19]); + currentHover.at(19) = true; + } + else { + currentHover.at(19) = false; + } +} + +bool Menu::checkOverButton(int x) { + return currentHover.at(x); } diff --git a/source-files/Shape.cpp b/source-files/Shape.cpp index 3730c32..b488c67 100644 --- a/source-files/Shape.cpp +++ b/source-files/Shape.cpp @@ -26,7 +26,7 @@ void Rectangle::Draw(ECGraphicViewImp& view) const { } // ellipse -Ellipse::Ellipse(int x1, int y1, int x2, int y2, ECGVColor color, int type): Shape(x1, y1, x2, y2, color, 1) { +Ellipse::Ellipse(int x1, int y1, int x2, int y2, ECGVColor color, int type): Shape(x1, y1, x2, y2, color, type) { xCenter = (x1 + x2) / 2; yCenter = (y1 + y2) / 2; xRadius = (double)fabs(x1 - x2) / 2.0; diff --git a/test-files/new-test.txt b/test-files/new-test.txt new file mode 100644 index 0000000..b6df23e --- /dev/null +++ b/test-files/new-test.txt @@ -0,0 +1,15 @@ +14 +0 4 544 455 544 352 452 352 452 455 0 +0 4 626 288 626 484 802 484 802 288 4 +0 4 215 586 215 728 409 728 409 586 5 +0 4 158 690 158 538 21 538 21 690 5 +1 396 399 131 70 6 +3 528 231 50 40 3 +3 676 193 45 34 3 +2 4 387 433 387 248 224 248 224 433 7 +2 4 741 617 741 521 588 521 588 617 7 +0 4 50 246 50 412 186 412 186 246 0 +0 4 504 702 504 653 437 653 437 702 0 +0 4 613 653 613 830 840 830 840 653 0 +0 4 439 890 439 790 245 790 245 890 0 +2 4 819 206 819 329 930 329 930 206 0 diff --git a/test-files/test.txt b/test-files/test.txt index 5962bb8..66d362b 100644 --- a/test-files/test.txt +++ b/test-files/test.txt @@ -1,2 +1,23 @@ -1 -0 4 179 344 179 592 464 592 464 344 0 +20 +4 2 +0 4 603 287 603 391 722 391 722 287 0 +0 4 517 429 517 677 802 677 802 429 0 +0 4 200 322 200 425 292 425 292 322 0 +0 4 265 511 265 450 170 450 170 511 0 +1 363 420 50 38 0 +1 350 513 45 28 0 +1 278 682 100 72 0 +2 4 173 642 173 553 78 553 78 642 0 +3 471 265 59 50 0 +0 4 830 231 830 317 939 317 939 231 3 +1 625 171 36 14 3 +1 471 371 17 17 3 +2 4 450 727 450 849 641 849 641 727 3 +3 216 882 90 61 3 +0 4 305 189 305 261 360 261 360 189 0 +0 4 467 149 467 177 495 177 495 149 0 +1 428 148 7 8 0 +2 4 366 136 366 161 393 161 393 136 0 +3 311 150 22 16 0 +2 4 56 373 56 477 153 477 153 373 0 +3 160 228 87 66 0