Skip to content

Commit

Permalink
start documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyler-Lentz committed May 2, 2024
1 parent 93bbd14 commit 61b1574
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 42 deletions.
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"server": {
"lobby_name": "Unnamed Lobby",
"lobby_broadcast": true,
"max_players": 2
"max_players": 1
},
"client": {
"default_name": "John Doe",
Expand Down
279 changes: 259 additions & 20 deletions include/client/gui/gui.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#pragma once

// #include "client/core.hpp"


// Include all gui headers so everyone else just needs to include this file
#include "client/gui/widget/options.hpp"
#include "client/gui/widget/type.hpp"
Expand All @@ -25,6 +22,11 @@ class Client;

namespace gui {

/**
* Enumeration for all of the different "screens" that can be rendered by the GUI. The GUI class
* itself doesn't contain any internal state related to any specific state, but instead takes
* a GUIState as input to determine what should be rendered to the screen.
*/
enum class GUIState {
NONE,
TITLE_SCREEN,
Expand All @@ -34,27 +36,154 @@ enum class GUIState {
GAME_ESC_MENU
};

/**
* Class which wraps around all of the GUI elements that should be rendered to the screen.
*
* The GUI can essentially be thought of as a collection of "Widgets" which all have "Handles" (or IDs).
* Each widget has all of the logic it needs to know its size, location, and how to render it.
* The GUI class acts as a container for all of these widgets and provides a helpful abstraction layer
* allowing the rest of the code to think in terms of "GUIState", since you can just tell the GUI
* to render a specific state, and it will do so.
*
* Here be Dragons
*/
class GUI {
public:
/// =<SETUP>========================================================================
///
/// These are the functions that need to be called to setup a GUI object. Doing anything
/// else before calling these will probably cause a crash.
///
/**
* @brief Stores the client pointer as a data member.
*
* Constructor for a GUI. This really does nothing except store a pointer to the Client object
* so that the GUI functions can easily access Client data members, due to the friend relationship
* of Client -> GUI.
*
* @param client Pointer to the client object. Note that GUI is a friend class to client, so GUI can
* access private client data members for ease of use.
*/
explicit GUI(Client* client);

/**
* @brief Initializes all of the necessary file loading for all of the GUI elements.
* Currently this is mainly the image loading and the font loading.
*
* @param text_shader Shader to use for text rendering
*/
bool init(GLuint text_shader);
/// ================================================================================

/// =<RENDERING>====================================================================
///
/// These series of functions are what you actually use to do the displaying and
/// rendering of the GUI. They should be called in the order they are listed in
/// this file.
///
/**
* @brief Wipes all of the previous frame's state
*
* Function that should be called at the beginning of a frame. Essentially it wipes
* all of the previous frame's widget data.
*/
void beginFrame();
/**
* @brief Adds widgets to the layout depending on the specified GUI state.
*
* @param state Current State of the GUI that should be rendered. This essentially
* corresponds to a specific "screen" that should be displayed
*/
void layoutFrame(GUIState state);
/**
* @brief Takes the current relevant input information and alters the GUI based on the
* how the inputs interact with all of the handlers assigned to the Widgets.
*
* NOTE: this must be called after adding all of the widgets to the screen, otherwise
* no event handling will work on those widgets added after calling this function.
*
* NOTE: currently this function takes a reference to the mouse down boolean because
* if it "captures" a mouse click then it sets that boolean to false, to prevent continued
* event triggers if the mouse is held down. In other words, we want "onClick" events
* to happen only on the initial click, not continually while the mouse is held down.
*
* NOTE: both of the x and y coordinates of the mouse passed into this function are in
* the GLFW coordinate space. In GLFW coordinates, the top left corner of the screen is
* (x=0,y=0). However, in the GUI world all of our coordinates are based off of
* (x=0,y=0) being in the bottom left corner. This is the only GUI function that takes
* GLFW coordinates,
*
* @param mouse_xpos x position of the mouse, in GLFW Coordinates
* @param mouse_ypos y position of the mouse, in GLFW Coordinates
* @param is_left_mouse_down reference to flag which stores whether or not the left mouse
* is down. Note: this is a reference so that if a click is captured it can toggle the
* mouse down flag to false, so that click doesn't get "double counted" in subsequent
* frames.
*/
void handleInputs(float mouse_xpos, float mouse_ypos, bool& is_left_mouse_down);
/**
* Renders the current state of the GUI to the screen, based on all of the widgets
* that have been added and any changes caused by inputs.
*/
void renderFrame();
/// ==============================================================================

/// =<WIDGET MANIPULATION>========================================================
///
/// These functions are concerned with adding, removing, and getting widgets to/from
/// the GUI.
///
/**
* @brief Adds the specified widget to the GUI.
*
* NOTE: the widget that is being passed in is of the type Widget::Ptr, which is
* a typedef for a std::unique_ptr<Widget>. To easily get a unique_ptr for a Widget,
* you can use the corresponding static `make` function provided by each widget
* implementation.
*
* @param widget Widget to add
* @returns A handle to the widget that was added, so it can be modifed/removed later
*/
widget::Handle addWidget(widget::Widget::Ptr&& widget);
std::unique_ptr<widget::Widget> removeWidget(widget::Handle handle);

bool shouldCaptureKeystrokes() const;
void setCaptureKeystrokes(bool should_capture);
void captureKeystroke(char c);
void captureBackspace();
void clearCapturedKeyboardInput();
std::string getCapturedKeyboardInput() const;

/**
* @brief Removes the specified widget from the GUI
*
* @param handle Handle to the widget that you want to remove
* @returns The widget that you removed, now isolated from the GUI.
*/
widget::Widget::Ptr removeWidget(widget::Handle handle);
/**
* @brief Borrows the specified widget from the GUI.
* This is especially useful inside of callback functions for widgets as you will
* already have the handle needed to initiate a borrow.
*
* NOTE: This function essentially returns the raw pointer for the specified widget.
* Technically you could do unspeakable things to this pointer, but as we are all
* bound by societal conventions so shall you too not break this taboo.
*
* NOTE: You should not attempt to save this pointer elsewhere. This pointer can be thought
* of as a "temporary" reference to the Widget, which will go out of scope
* between the time of calling and the next frame.
*
* NOTE: The template argument you specify essentially performs a dynamic cast on the
* returned pointer. You should not make your template argument a pointer itself, since
* the function return type already adds a pointer to it. You should only specify a
* specific kind of Widget if you are confident in what kind of widget it is (i.e. in
* an event handler). Otherwise, it is safe to just specify a widget::Widget as the
* template argument
*
* Example use:
* // Precondition: We know that `handle` is a valid handle to a widget::DynText
* auto widget = gui.borrowWidget<widget::DynText>(handle);
* // widget is of type `widget::DynText*`
*
* If you mess up and pass in the wrong template argument, then the pointer will
* end up being nullptr.
*
* @tparam Type of the widget that you are trying to borrow.
* @param handle Handle to the widget you want to borrow
* @returns Pointer to the widget specified by the handle, casted to
* be a pointer of the specified template type.
*/
template <typename W>
W* borrowWidget(widget::Handle handle) {
for (const auto& [_, widget] : this->widgets) {
Expand All @@ -68,13 +197,76 @@ class GUI {
<< "and means we are doing something very very bad." << std::endl;
std::exit(1);
}
/// ==============================================================================

void handleClick(float x, float y);
void handleHover(float x, float y);

void clearAll();
/// =<KEYBOARD INPUT>=============================================================
///
/// These functions are concerned with keyboard input for the TextInput widget.
/// Because of the way this is implemented, there can only be one TextInput widget
/// on the screen at one time. If there are more, they will end up sharing all of
/// the inputted text.
///
/**
* @brief Checks to see if the GUI is currently capturing keyboard input.
*
* NOTE: This shouldn't be called as a precondition for `captureKeystroke` because
* `captureKeystroke` will internally also check whether or not the GUI is
* capturing keystrokes.
*
* @returns true if the GUI is capturing keyboard input, false otherwise
*/
bool shouldCaptureKeystrokes() const;
/**
* @brief Toggles whether or not the GUI should be capturing keyboard input
*
* @param should_capture Whether or not the GUI should be capturing keyboard input
*/
void setCaptureKeystrokes(bool should_capture);
/**
* @brief Captures a keystroke as an ASCII char.
*
* Takes a keystroke as captured by the client's GLFW code, and adds it into the GUI.
* Internally, this will check that the ASCII value is between [32, 126], which is the
* set of meaningful ASCII values that we care about.
*
* NOTE: this function internally checks whether or not the GUI is capturing keyboard
* input. If the GUI is not currently capturing keyboard input, then this will do
* nothing. This means that you don't need to check `shouldCaptureKeystrokes` before
* calling this function.
*
* NOTE: This function does not handle backspaces. To handle backspaces, the
* `captureBackspace` function should be used instead.
*
* @param c ASCII char to capture
*/
void captureKeystroke(char c);
/**
* @brief If the GUI is capturing backspaces, then records a backspace press. This deletes
* the most recently captured keystroke in the GUI.
*/
void captureBackspace();
/**
* @brief Wipes all of the captured keyboard input from the internal state.
*/
void clearCapturedKeyboardInput();
/**
* @brief Returns all of the captured keyboard input without clearing it.
*
* @returns all of the captured keyboard input as an std::string
*/
std::string getCapturedKeyboardInput() const;
/// ==============================================================================

/// =<GETTERS>====================================================================
///
/// Getters for various private data members
///
/**
* @brief Getter for the font loader
* @returns shared pointer to the font loader
*/
std::shared_ptr<font::Loader> getFonts();
/// ==============================================================================

private:
widget::Handle next_handle {0};
Expand All @@ -89,13 +281,60 @@ class GUI {

Client* client;

/// =<INTERNAL HELPERS>==========================================================
/**
* @brief Performs a click on the specied coordinate in the GUI coordinate frame.
*
* This is what will call the click callback functions on the widgets.
*
* @param x x coordinate of click in GUI coordinates
* @param y y coordinate of click in GUI coordinates
*/
void _handleClick(float x, float y);
/**
* @brief Performs a mouse hover on the specied coordinate in the GUI coordinate frame.
*
* This is what will call the hover callback functions on the widgets.
*
* @param x x coordinate of click in GUI coordinates
* @param y y coordinate of click in GUI coordinates
*/
void _handleHover(float x, float y);
/// =============================================================================

/// =<GUI LAYOUTS>===============================================================
///
/// These are all of the internal helper functions which render a specified GUIState
/// layout. These are where all of the widget manipulation should occur.
///
/// NOTE: The widget manipulation functions are public, so you can also do further
/// widget manipulation outside of one of these functions. However, if you feel the
/// need to do this you should consider whether or not what you're doing makes more
/// sense to encode as a GUIState. The reason those functions are public are to do
/// more fine tuned GUI manipulation which may only make sense to do outside of these
/// preset "layouts".
///
/**
* @brief
*/
void _layoutTitleScreen();
/**
* @brief
*/
void _layoutLobbyBrowser();
/**
* @brief
*/
void _layoutLobby();
/**
* @brief
*/
void _layoutGameHUD();
/**
* @brief
*/
void _layoutGameEscMenu();
/// =============================================================================
};

using namespace gui;

}
}
Loading

0 comments on commit 61b1574

Please sign in to comment.