Repository: jwlodek/py_cui (language: python)

Issue:
Add a color parameter for message popup and remove default title for …
Modifications to popups behavior. 
When creating a Warning or an Error popup, `py_cui` added a default "Warning - " or "Error - " to the popup title. This can be annoying if you don't have any useful title to add as you'll be stuck with either "Warning - " with a dash hanging or something like "Warning - Warning".
Also, `show_warning_popup`  and `show_error_popup` are useful methods for quick YELLOW_ON_BLACK and RED_ON_BLACK popups but I think we shouldn't limit color choices for popups to white, yellow and black. So I added an optional `color` parameter which defaults to WHITE_ON_BLACK. 

The only actual change for devs will be the optional `color` argument, which still defaults to previous value hence ensuring backward compatibility.

- [x] I have read the contribution guidelines
- [x] CI Unit tests pass
- [x] New functions/classes have consistent docstrings

**What this pull request changes**

* Remove default  "Warning - "  and "Error -" in popup titles.
* Add an optional `color` parameter to `show_message_popup`.
* Modify doc accordingly.

**Issues fixed with this pull request**

None

MenuPopup does not allow non-string objects unlike the menu widget
**Describe the bug**
Currently, giving an object other than a string as an item to the menu popup, it will crash.

**To Reproduce**
Steps to reproduce the behavior:
1. Make a list of objects with `__str__` functions defined
2. Create a menu popup with the list as the input
3. Start the CUI

**Expected behavior**
Like the Menu Widget, the objects should be displayed with their `__str__` result

**Environment*:*
 - OS: N/A
 - Terminal: N/A
 - Version: v0.1.3



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index c920666..2522836 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,8 @@ npdoc2md/
 ecui/
 .coverage
 py_cui_log.txt
+*.log
 venv/
 .pytest_cache/
-demo.py
\ No newline at end of file
+demo.py
+*.log
diff --git a/README.md b/README.md
index c578913..9d163d7 100644
--- a/README.md
+++ b/README.md
@@ -23,47 +23,56 @@
 
 # py_cui
 
-`py_cui` is a python library meant to simplify writing command line user interfaces in python. It relies on `curses` for terminal rendering, which is traditionally a unix-specific, however, you may use the [windows-curses](https://github.com/zephyrproject-rtos/windows-curses) module to run `py_cui` on windows.
+`py_cui` is a python library meant to simplify writing console user interfaces in python. It relies on `curses` for terminal rendering, which is traditionally unix-specific, however, you may use the [windows-curses](https://github.com/zephyrproject-rtos/windows-curses) module to run `py_cui` on windows.
 
-The main advantage `py_cui` has over typical text-based user interface builders is that it relies on widgets and a grid layout manager like most traditional graphical user interfaces. You may define a grid size, and then drop predefined widgets onto it into specific grid locations. Widgets can also be stretched accross multiple grid rows and columns. If you've ever made a Tkinter GUI, you will feel right at home.
+The main advantage `py_cui` has over typical text-based user interface builders is that it relies on widgets and a grid layout manager like most traditional graphical user interfaces. You may define a grid size, and then drop predefined widgets onto it into specific grid locations. Widgets can also be stretched accross multiple grid rows and columns. If you've ever made a Tkinter GUI, you will feel right at home. In addition, `py_cui` has support for a rich collection of interactive popups such as menus, forms, file dialogs, and more.
 
 If you would like to contribute, feel free to make an issue or pull request after reading through the `CONTRIBUTING.md` file.
 
 ## Installation
 
 You may install `py_cui` via pip (use `pip3` if python 2 and 3 are installed side by side)
-```
+
+```bash
 pip install py-cui
 ```
-Make sure to run as root/sudo as required. Note that the library is titled `py_cui`, but the name of the pypi package is `py-cui`, though in most cases, installing with 
-```
+
+Make sure to run as root/sudo as required. Note that the library is titled `py_cui`, but the name of the pypi package is `py-cui`, though in most cases, installing with
+
+```bash
 pip install py_cui
 ```
+
 should work OK.
 
 If you would like to use `py_cui` directly from source, clone this repository with:
-```
+
+```bash
 git clone https://github.com/jwlodek/py_cui
 ```
+
 Then build/install with `pip`:
-```
+
+```bash
 cd py_cui
 pip install .
 ```
+
 If you would like to try it out without installing, first make sure that `curses` is installed (`windows-curses` if on windows), and then copy one of the example files into the top directory. You may then simply run it with `python3`.
 
 ## Examples
 
 Below are some screenshots and gifs of programs written with py_cui. Additional (simpler) examples are available in the `examples/` directory of this repository.
 
-**py_cui_2048 - A command line version of the classic 2048 game**
+### py_cui_2048 - A command line version of the classic 2048 game
 
 [py_cui_2048](https://github.com/jwlodek/py_cui_2048) is a demo of using `py_cui` to build simple CUI games and applications. The entire game was made in under 400 lines of code.
+
 <p align="center">
     <img src="docs/assets/py2048-demo.gif">
 </p>
 
-**recoverpy - A TUI that allows for recovering lost or deleted files**
+### recoverpy - A TUI that allows for recovering lost or deleted files
 
 [recoverpy](https://github.com/PabloLec/recoverpy) is a utility for recovering files on linux. It will search through every inode to find your requested file block.
 
@@ -71,9 +80,10 @@ Below are some screenshots and gifs of programs written with py_cui. Additional
     <img src="docs/assets/recoverpy-demo.gif">
 </p>
 
-**pyautogit - A command line interface for managing git repositories**
+### pyautogit - A command line interface for managing git repositories
 
 [pyautogit](https://github.com/jwlodek/pyautogit) is the first larger scale project written in `py_cui`. Feel free to use it as a guide to programming with `py_cui`.
+
 <p align="center">
     <img src="docs/assets/pyautogit-demo.gif">
 </p>
@@ -81,37 +91,47 @@ Below are some screenshots and gifs of programs written with py_cui. Additional
 ## Getting started
 
 The easiest way to get set up for developing a full `py_cui` application is to use the available `cookiecutter` template. First, install the `cookiecutter` tool:
-```
+
+```bash
 pip install cookiecutter
 ```
+
 Once it is installed, navigate to the directory in which you would like your projects to live, and run:
-```
+
+```bash
 cookiecutter https://github.com/jwlodek/py_cui_cookiecutter
 ```
+
 This will clone the template, and show prompts for several variables, including project name and description, as well as some developer information.
 Once it is done, you can enter your project directory, install it with pip, and run it, and you should be able to see a `Hello World` example:
-```
+
+```bash
 cd myproject
 pip install .
 myproject
 ```
+
 You are now ready to extend this template for your own application!
 
 ## Writing a PyCUI
 
 Basic usage of `py_cui` starts with creating a PyCUI object, and specifiying it's grid size. Keep in mind that grid cell height and width will be measured in terminal characters, not pixels, so there is a lower limit on legal grid size, and heights will be smaller values than widths. Create this object with:
-```
+
+```Python
 root = py_cui.PyCUI(7, 9)
 ```
+
 The above line will create a UI with 7 rows and 9 columns. Then, add widgets with the different add commands:
-```
+
+```Python
 label = root.add_label('Label Text', 0, 0)
 button = root.add_button('Button Text', 1, 2, column_span=2, command=my_function)
 ...
 ```
 
 Finally, start the CUI with
-```
+
+```Python
 root.start()
 ```
 
@@ -119,26 +139,28 @@ root.start()
 
 ## Using a PyCUI
 
-There are some basic rules that apply to all `py_cui` based interfaces. There are three key operating modes - overview mode, focus mode, and popup mode. 
+There are some basic rules that apply to all `py_cui` based interfaces. There are three key operating modes - overview mode, focus mode, and popup mode.
 
-**Overview Mode**
+### Overview Mode
 
 Overview mode is the main control view of the interface. In this mode you use the arrow keys to move between widgets, and you may select widgets with the `Enter` key. By default, you may also press buttons in this mode, unless `auto_focus_buttons` is set to false when the CUI was created.
 
-**Focus Mode**
+### Focus Mode
 
 When in focus mode, you enter into a particular widget (For example a text box.). Each widget has some predefined basic controls, such as arrow keys to scroll in a `ScrollMenu`. You may also add keybindings to functions for each particular widget. These keybindings will only apply to a widget if it is in focus mode. Enter focus mode by navigating to a widget in overview mode and hitting the `Enter` key. Return to overview mode from focus mode by pressing `Escape`.
 
-**Popup Mode**
+### Popup Mode
 
 Popup mode simply displays a popup over the rest of the UI. Each popup type behaves slightly differently. For example, you may exit an info popup mode by pressing `Space`, `Enter`, or `Escape`, while a loading popup will remain onscreen until whatever operation is being run is terminated.
 
 ## Unit Tests
 
 `py_cui` unit tests are written for `pytest`. Make sure `pytest` is installed, and simply run
-```
+
+```Bash
 pytest
 ```
+
 in the root directory to run all unit tests.
 
 ## Powered by py_cui
diff --git a/docs/DocstringGenerated/Colors.md b/docs/DocstringGenerated/Colors.md
index 025efb2..4c3d1c6 100644
--- a/docs/DocstringGenerated/Colors.md
+++ b/docs/DocstringGenerated/Colors.md
@@ -50,7 +50,7 @@ Class representing a text color rendering rule
 ### __init__
 
 ```python
-def __init__(self, regex, color, selected_color, rule_type, match_type, region, include_whitespace, logger)
+def __init__(self, regex: str, color: int, selected_color: int, rule_type: str, match_type: str, region: List[int], include_whitespace: bool, logger)
 ```
 
 Constructor for ColorRule object
@@ -77,7 +77,7 @@ Constructor for ColorRule object
 ### _check_match
 
 ```python
-def _check_match(self, line)
+def _check_match(self, line: str) -> bool
 ```
 
 Checks if the color rule matches a line
@@ -104,7 +104,7 @@ Checks if the color rule matches a line
 ### _generate_fragments_regex
 
 ```python
-def _generate_fragments_regex(self, widget, render_text, selected)
+def _generate_fragments_regex(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], render_text:str, selected) -> List[List[Union[int,str]]]
 ```
 
 Splits text into fragments based on regular expression
@@ -132,7 +132,7 @@ Splits text into fragments based on regular expression
 ### _split_text_on_region
 
 ```python
-def _split_text_on_region(self, widget, render_text, selected)
+def _split_text_on_region(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], render_text: str, selected) -> List[List[Union[str,int]]]:   # renderer._generate_text_color_fragments passes a uielement and not a widge
 ```
 
 Splits text into fragments based on region
@@ -160,7 +160,7 @@ Splits text into fragments based on region
 ### generate_fragments
 
 ```python
-def generate_fragments(self, widget, line, render_text, selected=False)
+def generate_fragments(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], line: str, render_text: str, selected=False) -> Tuple[List[List[Union[str,int]]],bool]
 ```
 
 Splits text into fragments if matched line to regex
diff --git a/docs/DocstringGenerated/Controls/Slider.md b/docs/DocstringGenerated/Controls/Slider.md
index 75947f6..10d440b 100644
--- a/docs/DocstringGenerated/Controls/Slider.md
+++ b/docs/DocstringGenerated/Controls/Slider.md
@@ -40,7 +40,7 @@ class SliderImplementation(py_cui.ui.UIImplementation)
 ### __init__
 
 ```python
-def __init__(self, min_val, max_val, init_val, step, logger)
+def __init__(self, min_val: int, max_val: int, init_val: int, step: int, logger)
 ```
 
 
@@ -54,7 +54,7 @@ def __init__(self, min_val, max_val, init_val, step, logger)
 ### set_bar_char
 
 ```python
-def set_bar_char(self, char)
+def set_bar_char(self, char: str) -> None
 ```
 
 
@@ -104,7 +104,7 @@ Steps up or down the value in offset fashion.
 ### get_slider_value
 
 ```python
-def get_slider_value(self)
+def get_slider_value(self) -> float
 ```
 
 
@@ -126,7 +126,7 @@ Returns current slider value.
 ### set_slider_step
 
 ```python
-def set_slider_step(self, step)
+def set_slider_step(self, step: int) -> None
 ```
 
 
@@ -204,7 +204,7 @@ def __init__(self, id, title, grid, row, column, row_span, column_span
 ### toggle_title
 
 ```python
-def toggle_title(self)
+def toggle_title(self) -> None
 ```
 
 Toggles visibility of the widget's name.
@@ -218,7 +218,7 @@ Toggles visibility of the widget's name.
 ### toggle_border
 
 ```python
-def toggle_border(self)
+def toggle_border(self) -> None
 ```
 
 Toggles visibility of the widget's border.
@@ -232,7 +232,7 @@ Toggles visibility of the widget's border.
 ### toggle_value
 
 ```python
-def toggle_value(self)
+def toggle_value(self) -> None
 ```
 
 Toggles visibility of the widget's current value in integer.
@@ -246,7 +246,7 @@ Toggles visibility of the widget's current value in integer.
 ### align_to_top
 
 ```python
-def align_to_top(self)
+def align_to_top(self) -> None
 ```
 
 Aligns widget height to top.
@@ -260,7 +260,7 @@ Aligns widget height to top.
 ### align_to_middle
 
 ```python
-def align_to_middle(self)
+def align_to_middle(self) -> None
 ```
 
 Aligns widget height to middle. default configuration.
@@ -274,7 +274,7 @@ Aligns widget height to middle. default configuration.
 ### align_to_bottom
 
 ```python
-def align_to_bottom(self)
+def align_to_bottom(self) -> None
 ```
 
 Aligns widget height to bottom.
@@ -288,7 +288,7 @@ Aligns widget height to bottom.
 ### _custom_draw_with_border
 
 ```python
-def _custom_draw_with_border(self, start_y: int, content: str)
+def _custom_draw_with_border(self, start_y: int, content: str) -> None
 ```
 
 
@@ -339,7 +339,7 @@ Internal implementation to generate progression bar.
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class draw function.
@@ -353,7 +353,7 @@ Override of base class draw function.
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 
diff --git a/docs/DocstringGenerated/Debug.md b/docs/DocstringGenerated/Debug.md
index 66814bc..3d94c21 100644
--- a/docs/DocstringGenerated/Debug.md
+++ b/docs/DocstringGenerated/Debug.md
@@ -8,6 +8,8 @@ Module containing py_cui logging utilities
 
  Class  | Doc
 -----|-----
+ LiveDebugImplementation(py_cui.ui.MenuImplementation) | Implementation class for the live debug menu - builds off of the scroll menu implementation
+ LiveDebugElement(py_cui.ui.UIElement, LiveDebugImplementation) | UIElement class for the live debug utility. extends from base UIElement class and LiveDebugImplementation
  PyCUILogger(logging.Logger) | Custom logger class for py_cui, extends the base logging.Logger Class
 
 #### Functions
@@ -23,7 +25,7 @@ Module containing py_cui logging utilities
 ### _enable_logging
 
 ```python
-def _enable_logging(logger, replace_log_file=True, filename='py_cui_log.txt', logging_level=logging.DEBUG)
+def _enable_logging(logger: 'PyCUILogger', replace_log_file: bool=True, filename: str='py_cui.log', logging_level=logging.DEBUG) 
 ```
 
 Function that creates basic logging configuration for selected logger
@@ -53,7 +55,7 @@ Function that creates basic logging configuration for selected logger
 ### _initialize_logger
 
 ```python
-def _initialize_logger(py_cui_root, name=None, custom_logger=True)
+def _initialize_logger(py_cui_root: 'py_cui.PyCUI', name: Optional[str]=None, custom_logger: bool=True) 
 ```
 
 Function that retrieves an instance of either the default or custom py_cui logger.
@@ -79,6 +81,215 @@ Function that retrieves an instance of either the default or custom py_cui logge
 
 
 
+## LiveDebugImplementation(py_cui.ui.MenuImplementation)
+
+```python
+class LiveDebugImplementation(py_cui.ui.MenuImplementation)
+```
+
+Implementation class for the live debug menu - builds off of the scroll menu implementation
+
+
+
+
+#### Attributes
+
+ Attribute  | Type  | Doc
+-----|----------|-----
+ level  |  int | Debug level at which to display messages. Can be separate from the default logging level
+ _buffer_size  |  List[str] | Number of log messages to keep buffered in the live debug window
+
+#### Methods
+
+ Method  | Doc
+-----|-----
+ print_to_buffer | Override of default MenuImplementation add_item function
+
+
+
+
+### __init__
+
+```python
+def __init__(self, parent_logger)
+```
+
+Initializer for LiveDebugImplementation
+
+
+
+
+
+
+
+### print_to_buffer
+
+```python
+def print_to_buffer(self, msg: str, log_level) -> None
+```
+
+Override of default MenuImplementation add_item function
+
+
+
+If items override the buffer pop the oldest log message
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ msg  |  str | Log message to add
+
+
+
+
+
+
+
+
+## LiveDebugElement(py_cui.ui.UIElement, LiveDebugImplementation)
+
+```python
+class LiveDebugElement(py_cui.ui.UIElement, LiveDebugImplementation)
+```
+
+UIElement class for the live debug utility. extends from base UIElement class and LiveDebugImplementation
+
+
+
+#### Methods
+
+ Method  | Doc
+-----|-----
+ get_absolute_start_pos | Override of base UI element class function. Sets start position relative to entire UI size
+ get_absolute_stop_pos | Override of base UI element class function. Sets stop position relative to entire UI size
+ _handle_mouse_press | Override of base class function, handles mouse press in menu
+ _handle_key_press | Override of base class function.
+ _draw | Overrides base class draw function. Mostly a copy of ScrollMenu widget - but reverse item list
+
+
+
+
+### __init__
+
+```python
+def __init__(self, parent_logger)
+```
+
+Initializer for LiveDebugElement class
+
+
+
+
+
+
+
+### get_absolute_start_pos
+
+```python
+def get_absolute_start_pos(self) -> Tuple[int,int]
+```
+
+Override of base UI element class function. Sets start position relative to entire UI size
+
+
+
+
+#### Returns
+
+ Return Variable  | Type  | Doc
+-----|----------|-----
+ start_x, start_y  |  int, int | Start position x, y coords in terminal characters
+
+
+
+
+
+### get_absolute_stop_pos
+
+```python
+def get_absolute_stop_pos(self) -> Tuple[int,int]
+```
+
+Override of base UI element class function. Sets stop position relative to entire UI size
+
+
+
+
+#### Returns
+
+ Return Variable  | Type  | Doc
+-----|----------|-----
+ stop_x, stop_y  |  int, int | Stop position x, y coords in terminal characters
+
+
+
+
+
+### _handle_mouse_press
+
+```python
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
+```
+
+Override of base class function, handles mouse press in menu
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ x, y  |  int | Coordinates of mouse press
+ mouse_event  |  int | Key code for py_cui mouse event
+
+
+
+
+
+### _handle_key_press
+
+```python
+def _handle_key_press(self, key_pressed: int) -> None
+```
+
+Override of base class function.
+
+
+
+Essentially the same as the ScrollMenu widget _handle_key_press, with the exception that Esc breaks
+out of live debug mode.
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ key_pressed  |  int | The keycode of the pressed key
+
+
+
+
+
+### _draw
+
+```python
+def _draw(self) -> None
+```
+
+Overrides base class draw function. Mostly a copy of ScrollMenu widget - but reverse item list
+
+
+
+
+
+
+
+
+
+
 ## PyCUILogger(logging.Logger)
 
 ```python
@@ -101,13 +312,16 @@ Custom logger class for py_cui, extends the base logging.Logger Class
 
  Method  | Doc
 -----|-----
- _assign_root_window | Attaches logger to the root window for live debugging
+ is_live_debug_enabled |
+ toggle_live_debug |
+ draw_live_debug | Function that draws the live debug UI element if applicable
+ _assign_root_window | Function that assigns a PyCUI root object to the logger. Important for live-debug hooks
  _get_debug_text | Function that generates full debug text for the log
- info | Adds stacktrace info to log
- debug | Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
- warn | Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
- error | Function that displays error messages live in status bar for py_cui logging
- toggle_live_debug | Toggles live debugging mode
+ info | Override of base logger info function to add hooks for live debug mode
+ debug | Override of base logger debug function to add hooks for live debug mode
+ warn | Override of base logger warn function to add hooks for live debug mode
+ error | Override of base logger error function to add hooks for live debug mode
+ critical | Override of base logger critical function to add hooks for live debug mode
 
 
 
@@ -133,16 +347,65 @@ Initializer for the PyCUILogger helper class
 
 
 
+### is_live_debug_enabled
+
+```python
+def is_live_debug_enabled(self)
+```
+
+
+
+
+
+
+
+
+
+### toggle_live_debug
+
+```python
+def toggle_live_debug(self)
+```
+
+
+
+
+
+
+
+
+
+### draw_live_debug
+
+```python
+def draw_live_debug(self)
+```
+
+Function that draws the live debug UI element if applicable
+
+
+
+
+
+
+
 ### _assign_root_window
 
 ```python
-def _assign_root_window(self, py_cui_root)
+def _assign_root_window(self, py_cui_root: 'py_cui.PyCUI') -> None
 ```
 
-Attaches logger to the root window for live debugging
+Function that assigns a PyCUI root object to the logger. Important for live-debug hooks
+
 
 
 
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ py_cui_root  |  PyCUI | Root PyCUI object for the application
+
 
 
 
@@ -150,7 +413,7 @@ Attaches logger to the root window for live debugging
 ### _get_debug_text
 
 ```python
-def _get_debug_text(self, text)
+def _get_debug_text(self, text: str) -> str
 ```
 
 Function that generates full debug text for the log
@@ -158,16 +421,29 @@ Function that generates full debug text for the log
 
 
 
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ text  |  str | Log message
+
+#### Returns
+
+ Return Variable  | Type  | Doc
+-----|----------|-----
+ msg  |  str | Log message with function, file, and line num info
+
+
 
 
 
 ### info
 
 ```python
-def info(self, text)
+def info(self, msg: Any, *args, **kwargs) -> None : # to overcome signature mismatch in erro
 ```
 
-Adds stacktrace info to log
+Override of base logger info function to add hooks for live debug mode
 
 
 
@@ -185,10 +461,10 @@ Adds stacktrace info to log
 ### debug
 
 ```python
-def debug(self, text)
+def debug(self, msg: str, *args, **kwargs) -> None
 ```
 
-Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
+Override of base logger debug function to add hooks for live debug mode
 
 
 
@@ -206,10 +482,10 @@ Function that allows for live debugging of py_cui programs by displaying log mes
 ### warn
 
 ```python
-def warn(self, text)
+def warn(self, msg: str, *args, **kwargs) -> None
 ```
 
-Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
+Override of base logger warn function to add hooks for live debug mode
 
 
 
@@ -227,10 +503,10 @@ Function that allows for live debugging of py_cui programs by displaying log mes
 ### error
 
 ```python
-def error(self, text)
+def error(self, msg: str, *args, **kwargs) -> None
 ```
 
-Function that displays error messages live in status bar for py_cui logging
+Override of base logger error function to add hooks for live debug mode
 
 
 
@@ -245,17 +521,24 @@ Function that displays error messages live in status bar for py_cui logging
 
 
 
-### toggle_live_debug
+### critical
 
 ```python
-def toggle_live_debug(self, level=logging.ERROR)
+def critical(self, msg: str, *args, **kwargs) -> None
 ```
 
-Toggles live debugging mode
+Override of base logger critical function to add hooks for live debug mode
 
 
 
 
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ text  |  str | The log text ot display
+
+
 
 
 
diff --git a/docs/DocstringGenerated/Dialogs/Filedialog.md b/docs/DocstringGenerated/Dialogs/Filedialog.md
index cafbbd5..73c33bf 100644
--- a/docs/DocstringGenerated/Dialogs/Filedialog.md
+++ b/docs/DocstringGenerated/Dialogs/Filedialog.md
@@ -28,7 +28,7 @@ Implementation, widget, and popup classes for file selection dialogs
 ### is_filepath_hidden
 
 ```python
-def is_filepath_hidden(path)
+def is_filepath_hidden(path:  str) -> bool
 ```
 
 Function checks if file or folder is considered "hidden"
@@ -86,7 +86,7 @@ Simple helper class defining a single file or directory
 ### __init__
 
 ```python
-def __init__(self, elem_type, name, fullpath, ascii_icons=False)
+def __init__(self, elem_type: str, name: str, fullpath: str, ascii_icons: bool=False)
 ```
 
 Intializer for FilDirElem
@@ -100,7 +100,7 @@ Intializer for FilDirElem
 ### get_path
 
 ```python
-def get_path(self)
+def get_path(self) -> str
 ```
 
 Getter for path
@@ -121,7 +121,7 @@ Getter for path
 ### __str__
 
 ```python
-def __str__(self)
+def __str__(self) -> str
 ```
 
 Override of to-string function
@@ -174,7 +174,7 @@ Extension of menu implementation that allows for listing files and dirs in a loc
 ### __init__
 
 ```python
-def __init__(self, initial_loc, dialog_type, ascii_icons, logger, limit_extensions = [], show_hidden=False)
+def __init__(self, initial_loc: str, dialog_type: str, ascii_icons, logger, limit_extensions: List[str] = [], show_hidden: bool=False)
 ```
 
 Initalizer for the file select menu implementation. Includes some logic for getting list of file and folders.
@@ -188,7 +188,7 @@ Initalizer for the file select menu implementation. Includes some logic for gett
 ### refresh_view
 
 ```python
-def refresh_view(self)
+def refresh_view(self) -> None
 ```
 
 Function that refreshes the current list of files and folders in view
@@ -237,7 +237,7 @@ Displays list of files and dirs in a given location
 ### __init__
 
 ```python
-def __init__(self, root, initial_dir, dialog_type, ascii_icons, title, color, command, renderer, logger, limit_extensions=[])
+def __init__(self, root, initial_dir, dialog_type: str, ascii_icons, title, color, command, renderer, logger, limit_extensions: List[str]=[])
 ```
 
 Initializer for MenuPopup. Uses MenuImplementation as base
@@ -251,7 +251,7 @@ Initializer for MenuPopup. Uses MenuImplementation as base
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute start position
@@ -272,7 +272,7 @@ Override of base function. Uses the parent element do compute start position
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute stop position
@@ -293,7 +293,7 @@ Override of base function. Uses the parent element do compute stop position
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base handle key press function
@@ -316,7 +316,7 @@ Enter key runs command, Escape key closes menu
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Overrides base class draw function
@@ -377,7 +377,7 @@ Initializer for the FormFieldElement class
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute start position
@@ -398,7 +398,7 @@ Override of base function. Uses the parent element do compute start position
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute stop position
@@ -419,7 +419,7 @@ Override of base function. Uses the parent element do compute stop position
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Override of base class. Updates text field variables for form field
@@ -433,7 +433,7 @@ Override of base class. Updates text field variables for form field
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Handles text input for the field. Called by parent
@@ -447,7 +447,7 @@ Handles text input for the field. Called by parent
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Draw function for the field. Called from parent. Essentially the same as a TextboxPopup
@@ -510,7 +510,7 @@ Initializer for Button Widget
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute start position
@@ -531,7 +531,7 @@ Override of base function. Uses the parent element do compute start position
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute stop position
@@ -552,7 +552,7 @@ Override of base function. Uses the parent element do compute stop position
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Handles mouse presses
@@ -566,6 +566,7 @@ Handles mouse presses
 -----|----------|-----
  x  |  int | x coordinate of click in characters
  y  |  int | y coordinate of click in characters
+ mouse_event  |  int | Mouse event keycode of mouse press
 
 
 
@@ -574,7 +575,7 @@ Handles mouse presses
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class, adds ENTER listener that runs the button's command
@@ -595,7 +596,7 @@ Override of base class, adds ENTER listener that runs the button's command
 ### perform_command
 
 ```python
-def perform_command(self)
+def perform_command(self) -> None
 ```
 
 
@@ -609,7 +610,7 @@ def perform_command(self)
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class draw function
@@ -666,7 +667,7 @@ Initializer for Internal form Popup
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class, close in parent instead of root
@@ -737,7 +738,7 @@ Initalizer for the FileDialogPopup
 ### _submit
 
 ```python
-def _submit(self, output)
+def _submit(self, output: str) -> None
 ```
 
 
@@ -751,7 +752,7 @@ def _submit(self, output)
 ### display_warning
 
 ```python
-def display_warning(self, message)
+def display_warning(self, message: str) -> None
 ```
 
 Helper function for showing internal popup warning message
@@ -772,7 +773,7 @@ Helper function for showing internal popup warning message
 ### output_valid
 
 ```python
-def output_valid(self, output)
+def output_valid(self, output) -> Tuple[bool,Optional[str]]
 ```
 
 
@@ -786,7 +787,7 @@ def output_valid(self, output)
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -807,7 +808,7 @@ Override of base class, computes position based on root dimensions
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -828,7 +829,7 @@ Override of base class, computes position based on root dimensions
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Override of base class function
@@ -844,7 +845,7 @@ Also updates all form field elements in the form
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed:int) -> None
 ```
 
 Override of base class. Here, we handle tabs, enters, and escapes
@@ -867,7 +868,7 @@ All other key presses are passed to the currently selected field element
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Override of base class function
@@ -890,7 +891,7 @@ Simply enters the appropriate field when mouse is pressed on it
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class.
diff --git a/docs/DocstringGenerated/Dialogs/Form.md b/docs/DocstringGenerated/Dialogs/Form.md
index 6e10b8f..7131e86 100644
--- a/docs/DocstringGenerated/Dialogs/Form.md
+++ b/docs/DocstringGenerated/Dialogs/Form.md
@@ -67,7 +67,7 @@ Class containing basic logic of a field in a form
 ### __init__
 
 ```python
-def __init__(self, fieldname, initial_text, password, required, logger)
+def __init__(self, fieldname: str, initial_text: str, password: bool, required: bool, logger)
 ```
 
 Initializer for base FormFields
@@ -81,7 +81,7 @@ Initializer for base FormFields
 ### get_fieldname
 
 ```python
-def get_fieldname(self)
+def get_fieldname(self) -> str
 ```
 
 Getter for field name
@@ -102,7 +102,7 @@ Getter for field name
 ### is_valid
 
 ```python
-def is_valid(self)
+def is_valid(self) -> Tuple[bool,Optional[str]]
 ```
 
 Function that checks if field is valid.
@@ -127,7 +127,7 @@ field types (ex. emails etc.)
 ### is_required
 
 ```python
-def is_required(self)
+def is_required(self) -> bool
 ```
 
 Checks if field is required
@@ -182,7 +182,7 @@ Extension of UI element representing an individual field in the form
 ### __init__
 
 ```python
-def __init__(self, parent_form, field_index, field, init_text, passwd, required, renderer, logger)
+def __init__(self, parent_form, field_index: int, field, init_text: str, passwd: bool, required: bool, renderer: 'py_cui.renderer.Renderer', logger)
 ```
 
 Initializer for the FormFieldElement class
@@ -196,7 +196,7 @@ Initializer for the FormFieldElement class
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute start position
@@ -217,7 +217,7 @@ Override of base function. Uses the parent element do compute start position
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base function. Uses the parent element do compute stop position
@@ -238,7 +238,7 @@ Override of base function. Uses the parent element do compute stop position
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Override of base class. Updates text field variables for form field
@@ -252,7 +252,7 @@ Override of base class. Updates text field variables for form field
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Handles text input for the field. Called by parent
@@ -266,7 +266,7 @@ Handles text input for the field. Called by parent
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Draw function for the field. Called from parent. Essentially the same as a TextboxPopup
@@ -318,7 +318,7 @@ Function fired when submit is called
 ### __init__
 
 ```python
-def __init__(self, field_implementations, required_fields, logger)
+def __init__(self, field_implementations: List['FormField'], required_fields: List[str], logger)
 ```
 
 Initializer for the FormImplemnentation class
@@ -332,7 +332,7 @@ Initializer for the FormImplemnentation class
 ### get_selected_form_index
 
 ```python
-def get_selected_form_index(self)
+def get_selected_form_index(self) -> int
 ```
 
 Getter for selected form index
@@ -353,7 +353,7 @@ Getter for selected form index
 ### set_selected_form_index
 
 ```python
-def set_selected_form_index(self, form_index)
+def set_selected_form_index(self, form_index: int) -> None
 ```
 
 Setter for selected form index
@@ -374,7 +374,7 @@ Setter for selected form index
 ### set_on_submit_action
 
 ```python
-def set_on_submit_action(self, on_submit_action)
+def set_on_submit_action(self, on_submit_action: Callable[[],Any])
 ```
 
 Setter for callback on submit
@@ -395,7 +395,7 @@ Setter for callback on submit
 ### jump_to_next_field
 
 ```python
-def jump_to_next_field(self)
+def jump_to_next_field(self) -> None
 ```
 
 Function used to jump between form fields
@@ -409,7 +409,7 @@ Function used to jump between form fields
 ### is_submission_valid
 
 ```python
-def is_submission_valid(self)
+def is_submission_valid(self) -> Tuple[bool,Optional[str]]
 ```
 
 Function that checks if all fields are filled out correctly
@@ -431,7 +431,7 @@ Function that checks if all fields are filled out correctly
 ### get
 
 ```python
-def get(self)
+def get(self) -> Dict[str,str]
 ```
 
 Gets values entered into field as a dictionary
@@ -497,7 +497,7 @@ A helper class for abstracting a message popup tied to a parent popup
 ### __init__
 
 ```python
-def __init__(self, parent, *args)
+def __init__(self, parent: 'FormPopup', *args)
 ```
 
 Initializer for Internal form Popup
@@ -511,7 +511,7 @@ Initializer for Internal form Popup
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class, close in parent instead of root
@@ -576,7 +576,7 @@ def __init__(self, root, fields, passwd_fields, required_fields, fields_init_tex
 ### get_num_fields
 
 ```python
-def get_num_fields(self)
+def get_num_fields(self) -> int
 ```
 
 Getter for number of fields
@@ -597,7 +597,7 @@ Getter for number of fields
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -618,7 +618,7 @@ Override of base class, computes position based on root dimensions
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -639,7 +639,7 @@ Override of base class, computes position based on root dimensions
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Override of base class function
@@ -655,7 +655,7 @@ Also updates all form field elements in the form
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class. Here, we handle tabs, enters, and escapes
@@ -678,7 +678,7 @@ All other key presses are passed to the currently selected field element
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Override of base class function
@@ -701,7 +701,7 @@ Simply enters the appropriate field when mouse is pressed on it
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class.
diff --git a/docs/DocstringGenerated/Grid.md b/docs/DocstringGenerated/Grid.md
index d9fe2c5..252ca26 100644
--- a/docs/DocstringGenerated/Grid.md
+++ b/docs/DocstringGenerated/Grid.md
@@ -34,6 +34,7 @@ Class representing the CUI grid
  _height, _width  |  int | The height, width in characters of the terminal window
  _offset_y, _offset_x  |  int | The number of additional characters found by height mod rows and width mod columns
  _row_height, _column_width  |  int | The number of characters in a single grid row, column
+ _title_bar_offset  |  int | Title bar row offset. Defaults to 1. Set to 0 if title bar is hidden.
  _logger  |  py_cui.debug.PyCUILogger | logger object for maintaining debug messages
 
 #### Methods
@@ -54,7 +55,7 @@ Class representing the CUI grid
 ### __init__
 
 ```python
-def __init__(self, num_rows, num_columns, height, width, logger)
+def __init__(self, num_rows: int, num_columns: int, height: int, width: int, logger: 'py_cui.debug.PyCUILogger')
 ```
 
 Constructor for the Grid class
@@ -78,7 +79,7 @@ Constructor for the Grid class
 ### get_dimensions
 
 ```python
-def get_dimensions(self)
+def get_dimensions(self) -> Tuple[int,int]
 ```
 
 Gets dimensions in rows/columns
@@ -100,7 +101,7 @@ Gets dimensions in rows/columns
 ### get_dimensions_absolute
 
 ```python
-def get_dimensions_absolute(self)
+def get_dimensions_absolute(self) -> Tuple[int,int]
 ```
 
 Gets dimensions of grid in terminal characters
@@ -122,7 +123,7 @@ Gets dimensions of grid in terminal characters
 ### get_offsets
 
 ```python
-def get_offsets(self)
+def get_offsets(self) -> Tuple[int,int]
 ```
 
 Gets leftover characters for x and y
@@ -144,7 +145,7 @@ Gets leftover characters for x and y
 ### get_cell_dimensions
 
 ```python
-def get_cell_dimensions(self)
+def get_cell_dimensions(self) -> Tuple[int,int]
 ```
 
 Gets size in characters of single (row, column) cell location
@@ -166,7 +167,7 @@ Gets size in characters of single (row, column) cell location
 ### set_num_rows
 
 ```python
-def set_num_rows(self, num_rows)
+def set_num_rows(self, num_rows: int) -> None
 ```
 
 Sets the grid row size
@@ -193,7 +194,7 @@ Sets the grid row size
 ### set_num_cols
 
 ```python
-def set_num_cols(self, num_columns)
+def set_num_cols(self, num_columns: int) -> None
 ```
 
 Sets the grid column size
@@ -220,7 +221,7 @@ Sets the grid column size
 ### update_grid_height_width
 
 ```python
-def update_grid_height_width(self, height, width)
+def update_grid_height_width(self, height: int, width: int)
 ```
 
 Update grid height and width. Allows for on-the-fly size editing
diff --git a/docs/DocstringGenerated/Keys.md b/docs/DocstringGenerated/Keys.md
index c48637c..5085a2f 100644
--- a/docs/DocstringGenerated/Keys.md
+++ b/docs/DocstringGenerated/Keys.md
@@ -17,7 +17,7 @@ Module containing constants and helper functions for dealing with keys.
 ### get_ascii_from_char
 
 ```python
-def get_ascii_from_char(char)
+def get_ascii_from_char(char: str) -> int
 ```
 
 Function that converts ascii code to character
@@ -44,7 +44,7 @@ Function that converts ascii code to character
 ### get_char_from_ascii
 
 ```python
-def get_char_from_ascii(key_num)
+def get_char_from_ascii(key_num: int) -> Optional[str]
 ```
 
 Function that converts a character to an ascii code
diff --git a/docs/DocstringGenerated/Popups.md b/docs/DocstringGenerated/Popups.md
index 63bacf2..93ccb9e 100644
--- a/docs/DocstringGenerated/Popups.md
+++ b/docs/DocstringGenerated/Popups.md
@@ -60,7 +60,7 @@ frame
 ### __init__
 
 ```python
-def __init__(self, root, title, text, color, renderer, logger)
+def __init__(self, root: 'py_cui.PyCUI', title: str, text: str, color: int, renderer: 'py_cui.renderer.Renderer', logger)
 ```
 
 Initializer for main popup class. Calls UIElement intialier, and sets some initial values
@@ -88,7 +88,7 @@ Function that increments an internal counter
 ### set_text
 
 ```python
-def set_text(self, text)
+def set_text(self, text: str) -> None 
 ```
 
 Sets popup text (message)
@@ -109,7 +109,7 @@ Sets popup text (message)
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -130,7 +130,7 @@ Override of base class, computes position based on root dimensions
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Override of base class, computes position based on root dimensions
@@ -151,7 +151,7 @@ Override of base class, computes position based on root dimensions
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Handles key presses when popup is open
@@ -174,7 +174,7 @@ By default, only closes popup when Escape is pressed
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Function that uses renderer to draw the popup
@@ -226,7 +226,7 @@ Initializer for MessagePopup
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Draw function for MessagePopup. Calls superclass draw()
@@ -284,7 +284,7 @@ Initializer for YesNoPopup
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int)
 ```
 
 Handle key press overwrite from superclass
@@ -341,6 +341,7 @@ Class representing a textbox popup
  Method  | Doc
 -----|-----
  update_height_width | Need to update all cursor positions on resize
+ _handle_mouse_press | Override of base class function, handles mouse press in menu
  _handle_key_press | Override of base handle key press function
  _draw | Override of base draw function
 
@@ -364,7 +365,7 @@ Initializer for textbox popup. Uses TextBoxImplementation as base
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Need to update all cursor positions on resize
@@ -375,10 +376,31 @@ Need to update all cursor positions on resize
 
 
 
+### _handle_mouse_press
+
+```python
+def _handle_mouse_press(self, x: int, y: int, mouse_event) -> None
+```
+
+Override of base class function, handles mouse press in menu
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ x, y  |  int | Coordinates of mouse press
+
+
+
+
+
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int)
 ```
 
 Override of base handle key press function
@@ -399,7 +421,7 @@ Override of base handle key press function
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base draw function
@@ -437,6 +459,7 @@ Allows for popup with several menu items to select from
 
  Method  | Doc
 -----|-----
+ _handle_mouse_press | Override of base class function, handles mouse press in menu
  _handle_key_press | Override of base handle key press function
  _draw | Overrides base class draw function
 
@@ -446,7 +469,7 @@ Allows for popup with several menu items to select from
 ### __init__
 
 ```python
-def __init__(self, root, items, title, color, command, renderer, logger, run_command_if_none)
+def __init__(self, root: 'py_cui.PyCUI', items, title, color, command, renderer, logger, run_command_if_none)
 ```
 
 Initializer for MenuPopup. Uses MenuImplementation as base
@@ -457,10 +480,31 @@ Initializer for MenuPopup. Uses MenuImplementation as base
 
 
 
+### _handle_mouse_press
+
+```python
+def _handle_mouse_press(self, x: int, y: int, mouse_event)
+```
+
+Override of base class function, handles mouse press in menu
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ x, y  |  int | Coordinates of mouse press
+
+
+
+
+
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base handle key press function
@@ -483,7 +527,7 @@ Enter key runs command, Escape key closes menu
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Overrides base class draw function
@@ -545,7 +589,7 @@ Initializer for LoadingIconPopup
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int)
 ```
 
 Override of base class function.
@@ -568,7 +612,7 @@ Loading icon popups cannot be cancelled, so we wish to avoid default behavior
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Overrides base draw function
@@ -630,7 +674,7 @@ Initializer for LoadingBarPopup
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int)
 ```
 
 Override of base class function.
@@ -653,7 +697,7 @@ Loading icon popups cannot be cancelled, so we wish to avoid default behavior
 ### _increment_counter
 
 ```python
-def _increment_counter(self)
+def _increment_counter(self) -> None
 ```
 
 Function that increments an internal counter
@@ -667,7 +711,7 @@ Function that increments an internal counter
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base draw function
diff --git a/docs/DocstringGenerated/PyCui.md b/docs/DocstringGenerated/PyCui.md
index 098bb80..f59d912 100644
--- a/docs/DocstringGenerated/PyCui.md
+++ b/docs/DocstringGenerated/PyCui.md
@@ -22,7 +22,7 @@ A python library for intuitively creating CUI/TUI interfaces with pre-built widg
 ### fit_text
 
 ```python
-def fit_text(width, text, center=False)
+def fit_text(width: int, text: str, center: bool = False) -> str
 ```
 
 Fits text to screen size
@@ -86,6 +86,7 @@ first create an instance of this class, and then add cells + widgets to it.
  set_refresh_timeout | Sets the CUI auto-refresh timeout to a number of seconds.
  set_on_draw_update_func | Adds a function that is fired during each draw call of the CUI
  set_widget_cycle_key | Assigns a key for automatically cycling through widgets in both focus and overview modes
+ set_toggle_live_debug_key |
  enable_logging | Function enables logging for py_cui library
  apply_widget_set | Function that replaces all widgets in a py_cui with those of a different widget set
  create_new_widget_set | Function that is used to create additional widget sets
@@ -107,6 +108,7 @@ first create an instance of this class, and then add cells + widgets to it.
  add_block_label | Function that adds a new block label to the CUI grid
  add_button | Function that adds a new button to the CUI grid
  add_slider | Function that adds a new label to the CUI grid
+ forget_widget | Function that is used to destroy or "forget" widgets. Forgotten widgets will no longer be drawn
  get_element_at_position | Returns containing widget for character position
  _get_horizontal_neighbors | Gets all horizontal (left, right) neighbor widgets
  _get_vertical_neighbors | Gets all vertical (up, down) neighbor widgets
@@ -130,8 +132,7 @@ first create an instance of this class, and then add cells + widgets to it.
  increment_loading_bar | Increments progress bar if loading bar popup is open
  stop_loading_popup | Leaves loading state, and closes popup.
  close_popup | Closes the popup, and resets focus
- _refresh_height_width | Function that updates the height and width of the CUI based on terminal window size
- get_absolute_size | Returns dimensions of CUI
+ _refresh_height_width | Function that updates the height and width of the CUI based on terminal window size."""
  _draw_widgets | Function that draws all of the widgets to the screen
  _draw_status_bars | Draws status bar and title bar
  _display_window_warning | Function that prints some basic error info if there is an error with the CUI
@@ -145,10 +146,10 @@ first create an instance of this class, and then add cells + widgets to it.
 ### __init__
 
 ```python
-def __init__(self, num_rows, num_cols, auto_focus_buttons=True
+def __init__(self, num_rows: int, num_cols: int, auto_focus_buttons: bool=True
 ```
 
-Constructor for PyCUI class
+Initializer for PyCUI class
 
 
 
@@ -159,7 +160,7 @@ Constructor for PyCUI class
 ### set_refresh_timeout
 
 ```python
-def set_refresh_timeout(self, timeout)
+def set_refresh_timeout(self, timeout: int)
 ```
 
 Sets the CUI auto-refresh timeout to a number of seconds.
@@ -180,7 +181,7 @@ Sets the CUI auto-refresh timeout to a number of seconds.
 ### set_on_draw_update_func
 
 ```python
-def set_on_draw_update_func(self, update_function)
+def set_on_draw_update_func(self, update_function: Callable[[],Any])
 ```
 
 Adds a function that is fired during each draw call of the CUI
@@ -201,7 +202,7 @@ Adds a function that is fired during each draw call of the CUI
 ### set_widget_cycle_key
 
 ```python
-def set_widget_cycle_key(self, forward_cycle_key=None, reverse_cycle_key=None)
+def set_widget_cycle_key(self, forward_cycle_key: int=None, reverse_cycle_key: int=None) -> None
 ```
 
 Assigns a key for automatically cycling through widgets in both focus and overview modes
@@ -219,10 +220,24 @@ Assigns a key for automatically cycling through widgets in both focus and overvi
 
 
 
+### set_toggle_live_debug_key
+
+```python
+def set_toggle_live_debug_key(self, toggle_debug_key: int) -> None
+```
+
+
+
+
+
+
+
+
+
 ### enable_logging
 
 ```python
-def enable_logging(self, log_file_path='py_cui_log.txt', logging_level = logging.DEBUG)
+def enable_logging(self, log_file_path: str='py_cui.log', logging_level = logging.DEBUG, live_debug_key: int = py_cui.keys.KEY_CTRL_D) -> None
 ```
 
 Function enables logging for py_cui library
@@ -244,7 +259,7 @@ Function enables logging for py_cui library
 ### apply_widget_set
 
 ```python
-def apply_widget_set(self, new_widget_set)
+def apply_widget_set(self, new_widget_set: py_cui.widget_set.WidgetSet) -> None
 ```
 
 Function that replaces all widgets in a py_cui with those of a different widget set
@@ -271,7 +286,7 @@ Function that replaces all widgets in a py_cui with those of a different widget
 ### create_new_widget_set
 
 ```python
-def create_new_widget_set(self, num_rows, num_cols)
+def create_new_widget_set(self, num_rows: int, num_cols: int) -> 'py_cui.widget_set.WidgetSet'
 ```
 
 Function that is used to create additional widget sets
@@ -302,7 +317,7 @@ for logging support.
 ### start
 
 ```python
-def start(self)
+def start(self) -> None
 ```
 
 Function that starts the CUI
@@ -316,7 +331,7 @@ Function that starts the CUI
 ### stop
 
 ```python
-def stop(self)
+def stop(self) -> None
 ```
 
 Function that stops the CUI, and fires the callback function.
@@ -332,7 +347,7 @@ Callback must be a no arg method
 ### run_on_exit
 
 ```python
-def run_on_exit(self, command)
+def run_on_exit(self, command: Callable[[],Any])
 ```
 
 Sets callback function on CUI exit. Must be a no-argument function or lambda function
@@ -353,7 +368,7 @@ Sets callback function on CUI exit. Must be a no-argument function or lambda fun
 ### set_title
 
 ```python
-def set_title(self, title)
+def set_title(self, title: str) -> None
 ```
 
 Sets the title bar text
@@ -374,7 +389,7 @@ Sets the title bar text
 ### set_status_bar_text
 
 ```python
-def set_status_bar_text(self, text)
+def set_status_bar_text(self, text: str) -> None
 ```
 
 Sets the status bar text when in overview mode
@@ -395,7 +410,7 @@ Sets the status bar text when in overview mode
 ### _initialize_colors
 
 ```python
-def _initialize_colors(self)
+def _initialize_colors(self) -> None
 ```
 
 Function for initialzing curses colors. Called when CUI is first created.
@@ -409,7 +424,7 @@ Function for initialzing curses colors. Called when CUI is first created.
 ### _initialize_widget_renderer
 
 ```python
-def _initialize_widget_renderer(self)
+def _initialize_widget_renderer(self) -> None
 ```
 
 Function that creates the renderer object that will draw each widget
@@ -423,7 +438,7 @@ Function that creates the renderer object that will draw each widget
 ### toggle_unicode_borders
 
 ```python
-def toggle_unicode_borders(self)
+def toggle_unicode_borders(self) -> None
 ```
 
 Function for toggling unicode based border rendering
@@ -437,7 +452,7 @@ Function for toggling unicode based border rendering
 ### set_widget_border_characters
 
 ```python
-def set_widget_border_characters(self, upper_left_corner, upper_right_corner, lower_left_corner, lower_right_corner, horizontal, vertical)
+def set_widget_border_characters(self, upper_left_corner: str, upper_right_corner: str, lower_left_corner: str, lower_right_corner: str, horizontal: str, vertical: str) -> None
 ```
 
 Function that can be used to set arbitrary border characters for drawing widget borders by renderer.
@@ -463,7 +478,7 @@ Function that can be used to set arbitrary border characters for drawing widget
 ### get_widgets
 
 ```python
-def get_widgets(self)
+def get_widgets(self) -> Dict[int,Optional['py_cui.widgets.Widget']]
 ```
 
 Function that gets current set of widgets
@@ -475,7 +490,7 @@ Function that gets current set of widgets
 
  Return Variable  | Type  | Doc
 -----|----------|-----
- widgets  |  dict of str -> widget | dictionary mapping widget IDs to object instances
+ widgets  |  dict of int -> widget | dictionary mapping widget IDs to object instances
 
 
 
@@ -484,7 +499,7 @@ Function that gets current set of widgets
 ### add_scroll_menu
 
 ```python
-def add_scroll_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0) -> py_cui.widgets.ScrollMenu
+def add_scroll_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0) -> 'py_cui.widgets.ScrollMenu'
 ```
 
 Function that adds a new scroll menu to the CUI grid
@@ -517,7 +532,7 @@ Function that adds a new scroll menu to the CUI grid
 ### add_checkbox_menu
 
 ```python
-def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0, checked_char='X') -> py_cui.widgets.CheckBoxMenu
+def add_checkbox_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0, checked_char: str='X') -> 'py_cui.widgets.CheckBoxMenu'
 ```
 
 Function that adds a new checkbox menu to the CUI grid
@@ -551,7 +566,7 @@ Function that adds a new checkbox menu to the CUI grid
 ### add_text_box
 
 ```python
-def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '', password = False) -> py_cui.widgets.TextBox
+def add_text_box(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '', password: bool = False) -> 'py_cui.widgets.TextBox'
 ```
 
 Function that adds a new text box to the CUI grid
@@ -586,7 +601,7 @@ Function that adds a new text box to the CUI grid
 ### add_text_block
 
 ```python
-def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '') -> py_cui.widgets.ScrollTextBlock
+def add_text_block(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '') -> 'py_cui.widgets.ScrollTextBlock'
 ```
 
 Function that adds a new text block to the CUI grid
@@ -620,7 +635,7 @@ Function that adds a new text block to the CUI grid
 ### add_label
 
 ```python
-def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0) -> py_cui.widgets.Label
+def add_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.Label'
 ```
 
 Function that adds a new label to the CUI grid
@@ -653,7 +668,7 @@ Function that adds a new label to the CUI grid
 ### add_block_label
 
 ```python
-def add_block_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, center=True) -> py_cui.widgets.BlockLabel
+def add_block_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, center: bool=True) -> 'py_cui.widgets.BlockLabel'
 ```
 
 Function that adds a new block label to the CUI grid
@@ -687,7 +702,7 @@ Function that adds a new block label to the CUI grid
 ### add_button
 
 ```python
-def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, command=None) -> py_cui.widgets.Button
+def add_button(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, command: Callable[[],Any]=None) -> 'py_cui.widgets.Button'
 ```
 
 Function that adds a new button to the CUI grid
@@ -721,7 +736,7 @@ Function that adds a new button to the CUI grid
 ### add_slider
 
 ```python
-def add_slider(self, title, row, column, row_span=1
+def add_slider(self, title: str, row: int, column: int, row_span: int=1
 ```
 
 Function that adds a new label to the CUI grid
@@ -755,10 +770,38 @@ Function that adds a new label to the CUI grid
 
 
 
+### forget_widget
+
+```python
+def forget_widget(self, widget : 'py_cui.widgets.Widget') -> None
+```
+
+Function that is used to destroy or "forget" widgets. Forgotten widgets will no longer be drawn
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ widget  |  py_cui.widgets.Widget | Widget to remove from the UI
+
+#### Raises
+
+ Error  | Type  | Doc
+-----|----------|-----
+ Unknown | TypeError | If input parameter is not of the py_cui widget type
+ Unknown | KeyError | If input widget does not exist in the current UI or has already been removed.
+
+
+
+
+
 ### get_element_at_position
 
 ```python
-def get_element_at_position(self, x, y)
+def get_element_at_position(self, x: int, y: int) -> Optional['py_cui.ui.UIElement']
 ```
 
 Returns containing widget for character position
@@ -786,7 +829,7 @@ Returns containing widget for character position
 ### _get_horizontal_neighbors
 
 ```python
-def _get_horizontal_neighbors(self, widget, direction)
+def _get_horizontal_neighbors(self, widget: 'py_cui.widgets.Widget', direction: int) -> Optional[List[int]]
 ```
 
 Gets all horizontal (left, right) neighbor widgets
@@ -814,7 +857,7 @@ Gets all horizontal (left, right) neighbor widgets
 ### _get_vertical_neighbors
 
 ```python
-def _get_vertical_neighbors(self, widget, direction)
+def _get_vertical_neighbors(self, widget: 'py_cui.widgets.Widget', direction: int) -> Optional[List[int]]
 ```
 
 Gets all vertical (up, down) neighbor widgets
@@ -842,7 +885,7 @@ Gets all vertical (up, down) neighbor widgets
 ### _check_if_neighbor_exists
 
 ```python
-def _check_if_neighbor_exists(self, direction)
+def _check_if_neighbor_exists(self, direction: int) -> Optional[int]
 ```
 
 Function that checks if widget has neighbor in specified cell.
@@ -862,7 +905,7 @@ Used for navigating CUI, as arrow keys find the immediate neighbor
 
  Return Variable  | Type  | Doc
 -----|----------|-----
- widget_id  |  str | The widget neighbor ID if found, None otherwise
+ widget_id  |  int | The widget neighbor ID if found, None otherwise
 
 
 
@@ -871,7 +914,7 @@ Used for navigating CUI, as arrow keys find the immediate neighbor
 ### get_selected_widget
 
 ```python
-def get_selected_widget(self)
+def get_selected_widget(self) -> Optional['py_cui.widgets.Widget']
 ```
 
 Function that gets currently selected widget
@@ -892,7 +935,7 @@ Function that gets currently selected widget
 ### set_selected_widget
 
 ```python
-def set_selected_widget(self, widget_id)
+def set_selected_widget(self, widget_id: int) -> None
 ```
 
 Function that sets the selected widget for the CUI
@@ -904,7 +947,7 @@ Function that sets the selected widget for the CUI
 
  Parameter  | Type  | Doc
 -----|----------|-----
- widget_id  |  str | the id of the widget to select
+ widget_id  |  int | the id of the widget to select
 
 
 
@@ -913,7 +956,7 @@ Function that sets the selected widget for the CUI
 ### lose_focus
 
 ```python
-def lose_focus(self)
+def lose_focus(self) -> None
 ```
 
 Function that forces py_cui out of focus mode.
@@ -929,7 +972,7 @@ After popup is called, focus is lost
 ### move_focus
 
 ```python
-def move_focus(self, widget, auto_press_buttons=True)
+def move_focus(self, widget: 'py_cui.widgets.Widget', auto_press_buttons: bool=True) -> None
 ```
 
 Moves focus mode to different widget
@@ -950,7 +993,7 @@ Moves focus mode to different widget
 ### _cycle_widgets
 
 ```python
-def _cycle_widgets(self, reverse=False)
+def _cycle_widgets(self, reverse: bool=False) -> None
 ```
 
 Function that is fired if cycle key is pressed to move to next widget
@@ -971,7 +1014,7 @@ Function that is fired if cycle key is pressed to move to next widget
 ### add_key_command
 
 ```python
-def add_key_command(self, key, command)
+def add_key_command(self, key: int, command: Callable[[],Any]) -> None
 ```
 
 Function that adds a keybinding to the CUI when in overview mode
@@ -993,7 +1036,7 @@ Function that adds a keybinding to the CUI when in overview mode
 ### show_message_popup
 
 ```python
-def show_message_popup(self, title, text)
+def show_message_popup(self, title: str, text: str, color: int = WHITE_ON_BLACK) -> None
 ```
 
 Shows a message popup
@@ -1007,6 +1050,7 @@ Shows a message popup
 -----|----------|-----
  title  |  str | Message title
  text  |  str | Message text
+ color |  int | Popup color with format FOREGOUND_ON_BACKGROUND. See colors module. Default: WHITE_ON_BLACK.
 
 
 
@@ -1015,7 +1059,7 @@ Shows a message popup
 ### show_warning_popup
 
 ```python
-def show_warning_popup(self, title, text)
+def show_warning_popup(self, title: str, text: str) -> None
 ```
 
 Shows a warning popup
@@ -1037,7 +1081,7 @@ Shows a warning popup
 ### show_error_popup
 
 ```python
-def show_error_popup(self, title, text)
+def show_error_popup(self, title: str, text: str) -> None
 ```
 
 Shows an error popup
@@ -1059,7 +1103,7 @@ Shows an error popup
 ### show_yes_no_popup
 
 ```python
-def show_yes_no_popup(self, title, command)
+def show_yes_no_popup(self, title: str, command: Callable[[bool], Any])
 ```
 
 Shows a yes/no popup.
@@ -1083,7 +1127,7 @@ The 'command' parameter must be a function with a single boolean parameter
 ### show_text_box_popup
 
 ```python
-def show_text_box_popup(self, title, command, password=False)
+def show_text_box_popup(self, title: str, command: Callable[[str], Any], password: bool=False)
 ```
 
 Shows a textbox popup.
@@ -1108,7 +1152,7 @@ The 'command' parameter must be a function with a single string parameter
 ### show_menu_popup
 
 ```python
-def show_menu_popup(self, title, menu_items, command, run_command_if_none=False)
+def show_menu_popup(self, title: str, menu_items: List[str], command: Callable[[str], Any], run_command_if_none: bool=False)
 ```
 
 Shows a menu popup.
@@ -1134,7 +1178,7 @@ The 'command' parameter must be a function with a single string parameter
 ### show_loading_icon_popup
 
 ```python
-def show_loading_icon_popup(self, title, message, callback=None)
+def show_loading_icon_popup(self, title: str, message: str, callback: Callable[[],Any]=None)
 ```
 
 Shows a loading icon popup
@@ -1157,7 +1201,7 @@ Shows a loading icon popup
 ### show_loading_bar_popup
 
 ```python
-def show_loading_bar_popup(self, title, num_items, callback=None)
+def show_loading_bar_popup(self, title: str, num_items: List[int], callback: Callable[[],Any]=None) -> None
 ```
 
 Shows loading bar popup.
@@ -1182,7 +1226,7 @@ Use 'increment_loading_bar' to show progress
 ### show_form_popup
 
 ```python
-def show_form_popup(self, title, fields, passwd_fields=[], required=[], callback=None)
+def show_form_popup(self, title: str, fields: List[str], passwd_fields: List[str]=[], required: List[str]=[], callback: Callable[[],Any]=None) -> None
 ```
 
 Shows form popup.
@@ -1209,7 +1253,7 @@ Used for inputting several fields worth of values
 ### show_filedialog_popup
 
 ```python
-def show_filedialog_popup(self, popup_type='openfile', initial_dir='.', callback=None, ascii_icons=True, limit_extensions=[])
+def show_filedialog_popup(self, popup_type: str='openfile', initial_dir: str ='.', callback: Callable[[],Any]=None, ascii_icons: bool=True, limit_extensions: List[str]=[]) -> None
 ```
 
 Shows form popup.
@@ -1223,11 +1267,11 @@ Used for inputting several fields worth of values
 
  Parameter  | Type  | Doc
 -----|----------|-----
- title  |  str | Message title
- fields  |  List[str] | Names of each individual field
- passwd_fields  |  List[str] | Field names that should have characters hidden
- required  |  List[str] | Fields that are required before submission
- callback=None  |  Function | If not none, fired after loading is completed. Must be a no-arg function
+ popup_type  |  str | Type of filedialog popup - either openfile, opendir, or saveas
+ initial_dir  |  os.PathLike | Path to directory in which to open the file dialog, default "."
+ callback=None  |  Callable | If not none, fired after loading is completed. Must be a no-arg function, default=None
+ ascii_icons  |  bool | Compatibility option - use ascii icons instead of unicode file/folder icons, default True
+ limit_extensions  |  List[str] | Only show files with extensions in this list if not empty. Default, []
 
 
 
@@ -1236,7 +1280,7 @@ Used for inputting several fields worth of values
 ### increment_loading_bar
 
 ```python
-def increment_loading_bar(self)
+def increment_loading_bar(self) -> None
 ```
 
 Increments progress bar if loading bar popup is open
@@ -1250,7 +1294,7 @@ Increments progress bar if loading bar popup is open
 ### stop_loading_popup
 
 ```python
-def stop_loading_popup(self)
+def stop_loading_popup(self) -> None
 ```
 
 Leaves loading state, and closes popup.
@@ -1266,7 +1310,7 @@ Must be called by user to escape loading.
 ### close_popup
 
 ```python
-def close_popup(self)
+def close_popup(self) -> None
 ```
 
 Closes the popup, and resets focus
@@ -1280,34 +1324,43 @@ Closes the popup, and resets focus
 ### _refresh_height_width
 
 ```python
-def _refresh_height_width(self, height, width)
+def _refresh_height_width(self) -> None
 ```
 
-Function that updates the height and width of the CUI based on terminal window size
-
+Function that updates the height and width of the CUI based on terminal window size."""
 
 
 
-#### Parameters
-
- Parameter  | Type  | Doc
------|----------|-----
- height  |  int | Window height in terminal characters
- width  |  int | Window width in terminal characters
-
+if self._simulated_terminal is None:
+if self._stdscr is None:
+term_size = shutil.get_terminal_size()
+height  = term_size.lines
+width   = term_size.columns
+else:
+# Use curses termsize when possible to fix resize bug on windows.
+height, width = self._stdscr.getmaxyx()
+else:
+height  = self._simulated_terminal[0]
+width   = self._simulated_terminal[1]
 
+height  = height - self.title_bar.get_height() - self.status_bar.get_height() - 2
 
+self._logger.debug(f'Resizing CUI to new dimensions {height} by {width}')
 
+self._height = height
+self._width  = width
+self._grid.update_grid_height_width(self._height, self._width)
+for widget_id in self.get_widgets().keys():
+widget = self.get_widgets()[widget_id] #using temp variable, for mypy
+if widget is not None:
+widget.update_height_width()
+if self._popup is not None:
+self._popup.update_height_width()
+if self._logger._live_debug_element is not None:
+self._logger._live_debug_element.update_height_width()
 
-### get_absolute_size
-
-```python
-def get_absolute_size(self)
-```
-
-Returns dimensions of CUI
-
-
+def get_absolute_size(self) -> Tuple[int,int]:
+"""Returns dimensions of CUI
 
 
 #### Returns
@@ -1323,7 +1376,7 @@ Returns dimensions of CUI
 ### _draw_widgets
 
 ```python
-def _draw_widgets(self)
+def _draw_widgets(self) -> None
 ```
 
 Function that draws all of the widgets to the screen
@@ -1337,7 +1390,7 @@ Function that draws all of the widgets to the screen
 ### _draw_status_bars
 
 ```python
-def _draw_status_bars(self, stdscr, height, width)
+def _draw_status_bars(self, stdscr, height: int, width: int) -> None
 ```
 
 Draws status bar and title bar
@@ -1360,7 +1413,7 @@ Draws status bar and title bar
 ### _display_window_warning
 
 ```python
-def _display_window_warning(self, stdscr, error_info)
+def _display_window_warning(self, stdscr, error_info: str) -> None
 ```
 
 Function that prints some basic error info if there is an error with the CUI
@@ -1382,7 +1435,7 @@ Function that prints some basic error info if there is an error with the CUI
 ### _handle_key_presses
 
 ```python
-def _handle_key_presses(self, key_pressed)
+def _handle_key_presses(self, key_pressed: int) -> None
 ```
 
 Function that handles all main loop key presses.
@@ -1403,7 +1456,7 @@ Function that handles all main loop key presses.
 ### _draw
 
 ```python
-def _draw(self, stdscr)
+def _draw(self, stdscr) -> None
 ```
 
 Main CUI draw loop called by start()
diff --git a/docs/DocstringGenerated/Renderer.md b/docs/DocstringGenerated/Renderer.md
index 3618c9a..59e5728 100644
--- a/docs/DocstringGenerated/Renderer.md
+++ b/docs/DocstringGenerated/Renderer.md
@@ -61,7 +61,7 @@ and text required for the cui. All of the functions supplied by the renderer cla
 ### __init__
 
 ```python
-def __init__(self, root, stdscr, logger)
+def __init__(self, root: 'py_cui.PyCUI', stdscr, logger)
 ```
 
 Constructor for renderer object
@@ -75,7 +75,7 @@ Constructor for renderer object
 ### _set_border_renderer_chars
 
 ```python
-def _set_border_renderer_chars(self, border_char_set)
+def _set_border_renderer_chars(self, border_char_set: Dict[str,str]) -> None
 ```
 
 Function that sets the border characters for ui_elements
@@ -96,7 +96,7 @@ Function that sets the border characters for ui_elements
 ### _set_bold
 
 ```python
-def _set_bold(self)
+def _set_bold(self) -> None
 ```
 
 Sets bold draw mode
@@ -110,7 +110,7 @@ Sets bold draw mode
 ### _unset_bold
 
 ```python
-def _unset_bold(self)
+def _unset_bold(self) -> None
 ```
 
 Unsets bold draw mode
@@ -124,7 +124,7 @@ Unsets bold draw mode
 ### set_color_rules
 
 ```python
-def set_color_rules(self, color_rules)
+def set_color_rules(self, color_rules) -> None
 ```
 
 Sets current color rules
@@ -145,7 +145,7 @@ Sets current color rules
 ### set_color_mode
 
 ```python
-def set_color_mode(self, color_mode)
+def set_color_mode(self, color_mode: int) -> None
 ```
 
 Sets the output color mode
@@ -166,7 +166,7 @@ Sets the output color mode
 ### unset_color_mode
 
 ```python
-def unset_color_mode(self, color_mode)
+def unset_color_mode(self, color_mode: int) -> None
 ```
 
 Unsets the output color mode
@@ -187,7 +187,7 @@ Unsets the output color mode
 ### reset_cursor
 
 ```python
-def reset_cursor(self, ui_element, fill=True)
+def reset_cursor(self, ui_element: 'py_cui.ui.UIElement', fill: bool=True) -> None
 ```
 
 Positions the cursor at the bottom right of the selected element
@@ -209,7 +209,7 @@ Positions the cursor at the bottom right of the selected element
 ### draw_cursor
 
 ```python
-def draw_cursor(self, cursor_y, cursor_x)
+def draw_cursor(self, cursor_y: int, cursor_x: int) -> None
 ```
 
 Draws the cursor at a particular location
@@ -230,7 +230,7 @@ Draws the cursor at a particular location
 ### draw_border
 
 ```python
-def draw_border(self, ui_element, fill=True, with_title=True)
+def draw_border(self, ui_element: 'py_cui.ui.UIElement', fill: bool=True, with_title: bool=True) -> None
 ```
 
 Draws ascii border around ui element
@@ -253,7 +253,7 @@ Draws ascii border around ui element
 ### _draw_border_top
 
 ```python
-def _draw_border_top(self, ui_element, y, with_title)
+def _draw_border_top(self, ui_element:'py_cui.ui.UIElement', y: int, with_title: bool) -> None
 ```
 
 Internal function for drawing top of border
@@ -276,7 +276,7 @@ Internal function for drawing top of border
 ### _draw_border_bottom
 
 ```python
-def _draw_border_bottom(self, ui_element, y)
+def _draw_border_bottom(self, ui_element: 'py_cui.ui.UIElement', y: int) -> None
 ```
 
 Internal function for drawing bottom of border
@@ -298,7 +298,7 @@ Internal function for drawing bottom of border
 ### _draw_blank_row
 
 ```python
-def _draw_blank_row(self, ui_element, y)
+def _draw_blank_row(self, ui_element: 'py_cui.ui.UIElement', y: int) -> None
 ```
 
 Internal function for drawing a blank row
@@ -320,7 +320,7 @@ Internal function for drawing a blank row
 ### _get_render_text
 
 ```python
-def _get_render_text(self, ui_element, line, centered, bordered, selected, start_pos)
+def _get_render_text(self, ui_element: 'py_cui.ui.UIElement', line: str, centered: bool, bordered: bool, selected, start_pos:int) -> List[List[Union[int,str]]]
 ```
 
 Internal function that computes the scope of the text that should be drawn
@@ -342,7 +342,7 @@ Internal function that computes the scope of the text that should be drawn
 
  Return Variable  | Type  | Doc
 -----|----------|-----
- render_text  |  str | The text shortened to fit within given space
+ render_text  |  str # to be checked, returns a List of [int,str] | The text shortened to fit within given space
 
 
 
@@ -351,7 +351,7 @@ Internal function that computes the scope of the text that should be drawn
 ### _generate_text_color_fragments
 
 ```python
-def _generate_text_color_fragments(self, ui_element, line, render_text, selected)
+def _generate_text_color_fragments(self, ui_element: 'py_cui.ui.UIElement', line: str, render_text: str, selected) -> List[List[Union[int,str]]]
 ```
 
 Function that applies color rules to text, dividing them if match is found
@@ -380,7 +380,7 @@ Function that applies color rules to text, dividing them if match is found
 ### draw_text
 
 ```python
-def draw_text(self, ui_element, line, y, centered = False, bordered = True, selected = False, start_pos = 0)
+def draw_text(self, ui_element: 'py_cui.ui.UIElement', line: str, y: int, centered: bool = False, bordered: bool = True, selected: bool = False, start_pos: int = 0)
 ```
 
 Function that draws ui_element text.
diff --git a/docs/DocstringGenerated/Ui.md b/docs/DocstringGenerated/Ui.md
index 59620ca..0cc03ba 100644
--- a/docs/DocstringGenerated/Ui.md
+++ b/docs/DocstringGenerated/Ui.md
@@ -84,7 +84,6 @@ class contains all links to the CUI engine.
  set_help_text | Sets status bar help text
  set_focus_text | Sets status bar focus text. Legacy function, overridden by set_focus_text
  _handle_key_press | Must be implemented by subclass. Used to handle keypresses
- add_mouse_press_handler | Sets a mouse press handler function
  _handle_mouse_press | Can be implemented by subclass. Used to handle mouse presses
  _draw | Must be implemented by subclasses. Uses renderer to draw element to terminal
  _assign_renderer | Function that assigns a renderer object to the element
@@ -110,7 +109,7 @@ Initializer for UIElement base class
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Must be implemented by subclass, computes the absolute coords of upper-left corner
@@ -124,7 +123,7 @@ Must be implemented by subclass, computes the absolute coords of upper-left corn
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Must be implemented by subclass, computes the absolute coords of bottom-right corner
@@ -138,7 +137,7 @@ Must be implemented by subclass, computes the absolute coords of bottom-right co
 ### get_absolute_dimensions
 
 ```python
-def get_absolute_dimensions(self)
+def get_absolute_dimensions(self) -> Tuple[int,int]
 ```
 
 Gets dimensions of element in terminal characters
@@ -159,7 +158,7 @@ Gets dimensions of element in terminal characters
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Function that refreshes position and dimensons on resize.
@@ -175,7 +174,7 @@ If necessary, make sure required widget attributes updated here as well.
 ### get_viewport_height
 
 ```python
-def get_viewport_height(self)
+def get_viewport_height(self) -> int
 ```
 
 Gets the height of the element viewport (height minus padding and borders)
@@ -196,7 +195,7 @@ Gets the height of the element viewport (height minus padding and borders)
 ### get_id
 
 ```python
-def get_id(self)
+def get_id(self) -> int
 ```
 
 Gets the element ID
@@ -217,7 +216,7 @@ Gets the element ID
 ### get_title
 
 ```python
-def get_title(self)
+def get_title(self) -> str
 ```
 
 Getter for ui element title
@@ -238,7 +237,7 @@ Getter for ui element title
 ### get_padding
 
 ```python
-def get_padding(self)
+def get_padding(self) -> Tuple[int,int]
 ```
 
 Gets ui element padding on in characters
@@ -259,7 +258,7 @@ Gets ui element padding on in characters
 ### get_start_position
 
 ```python
-def get_start_position(self)
+def get_start_position(self) -> Tuple[int,int]
 ```
 
 Gets coords of upper left corner
@@ -280,7 +279,7 @@ Gets coords of upper left corner
 ### get_stop_position
 
 ```python
-def get_stop_position(self)
+def get_stop_position(self) -> Tuple[int,int]
 ```
 
 Gets coords of lower right corner
@@ -301,7 +300,7 @@ Gets coords of lower right corner
 ### get_color
 
 ```python
-def get_color(self)
+def get_color(self) -> int
 ```
 
 Gets current element color
@@ -322,7 +321,7 @@ Gets current element color
 ### get_border_color
 
 ```python
-def get_border_color(self)
+def get_border_color(self) -> int
 ```
 
 Gets current element border color
@@ -343,7 +342,7 @@ Gets current element border color
 ### get_selected_color
 
 ```python
-def get_selected_color(self)
+def get_selected_color(self) -> int
 ```
 
 Gets current selected item color
@@ -364,7 +363,7 @@ Gets current selected item color
 ### is_selected
 
 ```python
-def is_selected(self)
+def is_selected(self) -> bool
 ```
 
 Get selected status
@@ -385,7 +384,7 @@ Get selected status
 ### get_renderer
 
 ```python
-def get_renderer(self)
+def get_renderer(self) -> 'py_cui.renderer.Renderer'
 ```
 
 Gets reference to renderer object
@@ -406,7 +405,7 @@ Gets reference to renderer object
 ### get_help_text
 
 ```python
-def get_help_text(self)
+def get_help_text(self) -> str
 ```
 
 Returns current help text
@@ -427,7 +426,7 @@ Returns current help text
 ### set_title
 
 ```python
-def set_title(self, title)
+def set_title(self, title: str)
 ```
 
 Function that sets the widget title.
@@ -448,7 +447,7 @@ Function that sets the widget title.
 ### set_color
 
 ```python
-def set_color(self, color)
+def set_color(self, color: int) -> None
 ```
 
 Sets element default color
@@ -469,7 +468,7 @@ Sets element default color
 ### set_border_color
 
 ```python
-def set_border_color(self, color)
+def set_border_color(self, color: int) -> None
 ```
 
 Sets element border color
@@ -490,7 +489,7 @@ Sets element border color
 ### set_focus_border_color
 
 ```python
-def set_focus_border_color(self, color)
+def set_focus_border_color(self, color: int) -> None
 ```
 
 Sets element border color if the current element
@@ -512,7 +511,7 @@ is focused
 ### set_selected_color
 
 ```python
-def set_selected_color(self, color)
+def set_selected_color(self, color: int) -> None
 ```
 
 Sets element sected color
@@ -533,7 +532,7 @@ Sets element sected color
 ### set_selected
 
 ```python
-def set_selected(self, selected)
+def set_selected(self, selected: bool) -> None
 ```
 
 Marks the UI element as selected or not selected
@@ -554,7 +553,7 @@ Marks the UI element as selected or not selected
 ### set_help_text
 
 ```python
-def set_help_text(self, help_text)
+def set_help_text(self, help_text: str) -> None
 ```
 
 Sets status bar help text
@@ -575,7 +574,7 @@ Sets status bar help text
 ### set_focus_text
 
 ```python
-def set_focus_text(self, focus_text)
+def set_focus_text(self, focus_text: str) -> None
 ```
 
 Sets status bar focus text. Legacy function, overridden by set_focus_text
@@ -607,31 +606,10 @@ Must be implemented by subclass. Used to handle keypresses
 
 
 
-### add_mouse_press_handler
-
-```python
-def add_mouse_press_handler(self, mouse_press_handler_func)
-```
-
-Sets a mouse press handler function
-
-
-
-
-#### Parameters
-
- Parameter  | Type  | Doc
------|----------|-----
- mouse_press_handler_func  |  function / lambda function | Function that takes 2 parameters: x and y of a mouse press. Executes when mouse pressed and element is selected
-
-
-
-
-
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x, y, mouse_event)
 ```
 
 Can be implemented by subclass. Used to handle mouse presses
@@ -666,7 +644,7 @@ Must be implemented by subclasses. Uses renderer to draw element to terminal
 ### _assign_renderer
 
 ```python
-def _assign_renderer(self, renderer)
+def _assign_renderer(self, renderer: 'py_cui.renderer.Renderer', quiet: bool=False) 
 ```
 
 Function that assigns a renderer object to the element
@@ -695,7 +673,7 @@ Function that assigns a renderer object to the element
 ### _contains_position
 
 ```python
-def _contains_position(self, x, y)
+def _contains_position(self, x: int, y: int) -> bool
 ```
 
 Checks if character position is within element.
@@ -813,7 +791,7 @@ UI implementation for a single-row textbox input
 ### __init__
 
 ```python
-def __init__(self, initial_text, password, logger)
+def __init__(self, initial_text: str, password: bool , logger)
 ```
 
 Initializer for the TextBoxImplementation base class
@@ -827,7 +805,7 @@ Initializer for the TextBoxImplementation base class
 ### get_initial_cursor_pos
 
 ```python
-def get_initial_cursor_pos(self)
+def get_initial_cursor_pos(self) -> int
 ```
 
 Gets initial cursor position
@@ -848,7 +826,7 @@ Gets initial cursor position
 ### get_cursor_text_pos
 
 ```python
-def get_cursor_text_pos(self)
+def get_cursor_text_pos(self) -> int
 ```
 
 Gets current position of cursor relative to text
@@ -869,7 +847,7 @@ Gets current position of cursor relative to text
 ### get_cursor_limits
 
 ```python
-def get_cursor_limits(self)
+def get_cursor_limits(self) -> Tuple[int,int]
 ```
 
 Gets cursor extreme points in terminal position
@@ -890,7 +868,7 @@ Gets cursor extreme points in terminal position
 ### get_cursor_position
 
 ```python
-def get_cursor_position(self)
+def get_cursor_position(self) -> Tuple[int,int]
 ```
 
 Returns current cursor poition
@@ -911,7 +889,7 @@ Returns current cursor poition
 ### get_viewport_width
 
 ```python
-def get_viewport_width(self)
+def get_viewport_width(self) -> int
 ```
 
 Gets the width of the textbox viewport
@@ -932,7 +910,7 @@ Gets the width of the textbox viewport
 ### set_text
 
 ```python
-def set_text(self, text)
+def set_text(self, text: str)
 ```
 
 Sets the value of the text. Overwrites existing text
@@ -953,7 +931,7 @@ Sets the value of the text. Overwrites existing text
 ### get
 
 ```python
-def get(self)
+def get(self) -> str
 ```
 
 Gets value of the text in the textbox
@@ -974,7 +952,7 @@ Gets value of the text in the textbox
 ### clear
 
 ```python
-def clear(self)
+def clear(self) -> None
 ```
 
 Clears the text in the textbox
@@ -988,7 +966,7 @@ Clears the text in the textbox
 ### _move_left
 
 ```python
-def _move_left(self)
+def _move_left(self) -> None
 ```
 
 Shifts the cursor the the left. Internal use only
@@ -1002,7 +980,7 @@ Shifts the cursor the the left. Internal use only
 ### _move_right
 
 ```python
-def _move_right(self)
+def _move_right(self) -> None
 ```
 
 Shifts the cursor the the right. Internal use only
@@ -1016,7 +994,7 @@ Shifts the cursor the the right. Internal use only
 ### _insert_char
 
 ```python
-def _insert_char(self, key_pressed)
+def _insert_char(self, key_pressed: int) -> None
 ```
 
 Inserts char at cursor position. Internal use only
@@ -1037,7 +1015,7 @@ Inserts char at cursor position. Internal use only
 ### _jump_to_start
 
 ```python
-def _jump_to_start(self)
+def _jump_to_start(self) -> None
 ```
 
 Jumps to the start of the textbox. Internal use only
@@ -1051,7 +1029,7 @@ Jumps to the start of the textbox. Internal use only
 ### _jump_to_end
 
 ```python
-def _jump_to_end(self)
+def _jump_to_end(self) -> None
 ```
 
 Jumps to the end to the textbox. Internal use only
@@ -1065,7 +1043,7 @@ Jumps to the end to the textbox. Internal use only
 ### _erase_char
 
 ```python
-def _erase_char(self)
+def _erase_char(self) -> None
 ```
 
 Erases character at textbox cursor. Internal Use only
@@ -1079,7 +1057,7 @@ Erases character at textbox cursor. Internal Use only
 ### _delete_char
 
 ```python
-def _delete_char(self)
+def _delete_char(self) -> None
 ```
 
 Deletes character to right of texbox cursor. Internal use only
@@ -1120,7 +1098,8 @@ Analogous to a RadioButton
  Method  | Doc
 -----|-----
  clear | Clears all items from the Scroll Menu
- get_selected_item_index | Gets the currently selected item
+ set_on_selection_change_event | Function that sets the function fired when menu selection changes.
+ _process_selection_change_event | Function that executes on-selection change event either with the current menu item, or with no-args"""
  set_selected_item_index | Sets the currently selected item
  _scroll_up | Function that scrolls the view up in the scroll menu
  _scroll_down | Function that scrolls the view down in the scroll menu
@@ -1156,7 +1135,7 @@ Initializer for MenuImplementation base class
 ### clear
 
 ```python
-def clear(self)
+def clear(self) -> None
 ```
 
 Clears all items from the Scroll Menu
@@ -1167,16 +1146,68 @@ Clears all items from the Scroll Menu
 
 
 
-### get_selected_item_index
+### set_on_selection_change_event
+
+```python
+def set_on_selection_change_event(self, on_selection_change_event: Callable[[Any],Any])
+```
+
+Function that sets the function fired when menu selection changes.
+
+
+
+Event function must take 0 or 1 parameters. If 1 parameter, the new selcted item will be passed in.
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ on_selection_change_event  |  Callable | Callable function that takes in as an argument the newly selected element
+
+#### Raises
+
+ Error  | Type  | Doc
+-----|----------|-----
+ Unknown | TypeError | Raises a type error if event function is not callable
+
+
+
+
+
+### _process_selection_change_event
 
 ```python
-def get_selected_item_index(self)
+def _process_selection_change_event(self)
 ```
 
-Gets the currently selected item
+Function that executes on-selection change event either with the current menu item, or with no-args"""
 
 
 
+# Identify num of args from callable. This allows for user to create commands that take in x, y
+# coords of the mouse press as input
+num_args = 0
+try:
+num_args = len(inspect.signature(self._on_selection_change).parameters)
+except ValueError:
+self._logger.error('Failed to get on_selection_change signature!')
+except TypeError:
+self._logger.error('Type of object not supported for signature identification!')
+
+# Depending on the number of parameters for the self._on_selection_change, pass in the x and y
+# values, or do nothing
+if num_args == 1:
+self._on_selection_change(self.get())
+elif num_args == 0:
+self._on_selection_change()
+else:
+raise ValueError('On selection change event must accept either 0 or 1 parameters!')
+
+
+def get_selected_item_index(self) -> int:
+"""Gets the currently selected item
+
 
 #### Returns
 
@@ -1191,7 +1222,7 @@ Gets the currently selected item
 ### set_selected_item_index
 
 ```python
-def set_selected_item_index(self, selected_item_index)
+def set_selected_item_index(self, selected_item_index: int) -> None
 ```
 
 Sets the currently selected item
@@ -1212,7 +1243,7 @@ Sets the currently selected item
 ### _scroll_up
 
 ```python
-def _scroll_up(self)
+def _scroll_up(self) -> None
 ```
 
 Function that scrolls the view up in the scroll menu
@@ -1226,7 +1257,7 @@ Function that scrolls the view up in the scroll menu
 ### _scroll_down
 
 ```python
-def _scroll_down(self, viewport_height)
+def _scroll_down(self, viewport_height: int) -> None
 ```
 
 Function that scrolls the view down in the scroll menu
@@ -1249,7 +1280,7 @@ TODO: Viewport height should be calculated internally, and not rely on a paramet
 ### _jump_up
 
 ```python
-def _jump_up(self)
+def _jump_up(self) -> None
 ```
 
 Function for jumping up menu several spots at a time
@@ -1263,7 +1294,7 @@ Function for jumping up menu several spots at a time
 ### _jump_down
 
 ```python
-def _jump_down(self, viewport_height)
+def _jump_down(self, viewport_height: int) -> None
 ```
 
 Function for jumping down the menu several spots at a time
@@ -1284,7 +1315,7 @@ Function for jumping down the menu several spots at a time
 ### _jump_to_top
 
 ```python
-def _jump_to_top(self)
+def _jump_to_top(self) -> None
 ```
 
 Function that jumps to the top of the menu
@@ -1298,7 +1329,7 @@ Function that jumps to the top of the menu
 ### _jump_to_bottom
 
 ```python
-def _jump_to_bottom(self, viewport_height)
+def _jump_to_bottom(self, viewport_height: int) -> None
 ```
 
 Function that jumps to the bottom of the menu
@@ -1319,7 +1350,7 @@ Function that jumps to the bottom of the menu
 ### add_item
 
 ```python
-def add_item(self, item)
+def add_item(self, item: Any) -> None
 ```
 
 Adds an item to the menu.
@@ -1340,7 +1371,7 @@ Adds an item to the menu.
 ### add_item_list
 
 ```python
-def add_item_list(self, item_list)
+def add_item_list(self, item_list: List[Any]) -> None
 ```
 
 Adds a list of items to the scroll menu.
@@ -1361,7 +1392,7 @@ Adds a list of items to the scroll menu.
 ### remove_selected_item
 
 ```python
-def remove_selected_item(self)
+def remove_selected_item(self) -> None
 ```
 
 Function that removes the selected item from the scroll menu.
@@ -1375,7 +1406,7 @@ Function that removes the selected item from the scroll menu.
 ### remove_item
 
 ```python
-def remove_item(self, item)
+def remove_item(self, item: Any) -> None
 ```
 
 Function that removes a specific item from the menu
@@ -1396,7 +1427,7 @@ Function that removes a specific item from the menu
 ### get_item_list
 
 ```python
-def get_item_list(self)
+def get_item_list(self) -> List[Any]
 ```
 
 Function that gets list of items in a scroll menu
@@ -1417,7 +1448,7 @@ Function that gets list of items in a scroll menu
 ### get
 
 ```python
-def get(self)
+def get(self) -> Optional[Any]
 ```
 
 Function that gets the selected item from the scroll menu
@@ -1438,7 +1469,7 @@ Function that gets the selected item from the scroll menu
 ### set_selected_item
 
 ```python
-def set_selected_item(self, selected_item)
+def set_selected_item(self, selected_item: Any)
 ```
 
 Function that replaces the currently selected item with a new item
@@ -1484,7 +1515,9 @@ Class representing checkbox menu ui implementation
  add_item | Extends base class function, item is added and marked as unchecked to start
  remove_selected_item | Removes selected item from item list and selected item dictionary
  remove_item | Removes item from item list and selected item dict
+ toggle_item_checked | Function that marks an item as selected
  mark_item_as_checked | Function that marks an item as selected
+ mark_item_as_not_checked | Function that marks an item as selected
 
 
 
@@ -1506,7 +1539,7 @@ Initializer for the checkbox menu implementation
 ### add_item
 
 ```python
-def add_item(self, item)
+def add_item(self, item: Any) -> None
 ```
 
 Extends base class function, item is added and marked as unchecked to start
@@ -1527,7 +1560,7 @@ Extends base class function, item is added and marked as unchecked to start
 ### remove_selected_item
 
 ```python
-def remove_selected_item(self)
+def remove_selected_item(self) -> None
 ```
 
 Removes selected item from item list and selected item dictionary
@@ -1541,7 +1574,7 @@ Removes selected item from item list and selected item dictionary
 ### remove_item
 
 ```python
-def remove_item(self, item)
+def remove_item(self, item) -> None
 ```
 
 Removes item from item list and selected item dict
@@ -1559,10 +1592,52 @@ Removes item from item list and selected item dict
 
 
 
+### toggle_item_checked
+
+```python
+def toggle_item_checked(self, item: Any)
+```
+
+Function that marks an item as selected
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ item  |  object | Toggle item checked state
+
+
+
+
+
 ### mark_item_as_checked
 
 ```python
-def mark_item_as_checked(self, item)
+def mark_item_as_checked(self, item: Any) -> None
+```
+
+Function that marks an item as selected
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ item  |  object | Toggle item checked state
+
+
+
+
+
+### mark_item_as_not_checked
+
+```python
+def mark_item_as_not_checked(self, item) -> None
 ```
 
 Function that marks an item as selected
@@ -1574,7 +1649,7 @@ Function that marks an item as selected
 
  Parameter  | Type  | Doc
 -----|----------|-----
- item  |  object | Mark item as checked
+ item  |  object | Item to uncheck
 
 
 
@@ -1642,7 +1717,7 @@ Currently only implemented in widget form, though popup form is possible.
 ### __init__
 
 ```python
-def __init__(self, initial_text, logger)
+def __init__(self, initial_text: str, logger)
 ```
 
 Initializer for TextBlockImplementation base class
@@ -1658,7 +1733,7 @@ Zeros attributes, and parses initial text
 ### get_viewport_start_pos
 
 ```python
-def get_viewport_start_pos(self)
+def get_viewport_start_pos(self) -> Tuple[int,int]
 ```
 
 Gets upper left corner position of viewport
@@ -1679,7 +1754,7 @@ Gets upper left corner position of viewport
 ### get_viewport_dims
 
 ```python
-def get_viewport_dims(self)
+def get_viewport_dims(self) -> Tuple[int,int]
 ```
 
 Gets viewport dimensions in characters
@@ -1700,7 +1775,7 @@ Gets viewport dimensions in characters
 ### get_cursor_text_pos
 
 ```python
-def get_cursor_text_pos(self)
+def get_cursor_text_pos(self) -> Tuple[int,int]
 ```
 
 Gets cursor postion relative to text
@@ -1721,7 +1796,7 @@ Gets cursor postion relative to text
 ### get_abs_cursor_position
 
 ```python
-def get_abs_cursor_position(self)
+def get_abs_cursor_position(self) -> Tuple[int,int]
 ```
 
 Gets absolute cursor position in terminal characters
@@ -1742,7 +1817,7 @@ Gets absolute cursor position in terminal characters
 ### get_cursor_limits_vertical
 
 ```python
-def get_cursor_limits_vertical(self)
+def get_cursor_limits_vertical(self) -> Tuple[int,int]
 ```
 
 Gets limits for cursor in vertical direction
@@ -1763,7 +1838,7 @@ Gets limits for cursor in vertical direction
 ### get_cursor_limits_horizontal
 
 ```python
-def get_cursor_limits_horizontal(self)
+def get_cursor_limits_horizontal(self) -> Tuple[int,int]
 ```
 
 Gets limits for cursor in horizontal direction
@@ -1784,7 +1859,7 @@ Gets limits for cursor in horizontal direction
 ### get
 
 ```python
-def get(self)
+def get(self) -> str
 ```
 
 Gets all of the text in the textblock and returns it
@@ -1805,7 +1880,7 @@ Gets all of the text in the textblock and returns it
 ### write
 
 ```python
-def write(self, text)
+def write(self, text: str) -> None
 ```
 
 Function used for writing text to the text block
@@ -1826,7 +1901,7 @@ Function used for writing text to the text block
 ### clear
 
 ```python
-def clear(self)
+def clear(self) -> None
 ```
 
 Function that clears the text block
@@ -1840,7 +1915,7 @@ Function that clears the text block
 ### get_current_line
 
 ```python
-def get_current_line(self)
+def get_current_line(self) -> str
 ```
 
 Returns the line on which the cursor currently resides
@@ -1861,7 +1936,7 @@ Returns the line on which the cursor currently resides
 ### set_text
 
 ```python
-def set_text(self, text)
+def set_text(self, text: str) -> None
 ```
 
 Function that sets the text for the textblock.
@@ -1884,7 +1959,7 @@ Note that this will overwrite any existing text
 ### set_text_line
 
 ```python
-def set_text_line(self, text)
+def set_text_line(self, text: str) -> None
 ```
 
 Function that sets the current line's text.
@@ -1907,7 +1982,7 @@ Meant only for internal use
 ### _move_left
 
 ```python
-def _move_left(self)
+def _move_left(self) -> None
 ```
 
 Function that moves the cursor/text position one location to the left
@@ -1921,7 +1996,7 @@ Function that moves the cursor/text position one location to the left
 ### _move_right
 
 ```python
-def _move_right(self)
+def _move_right(self) -> None
 ```
 
 Function that moves the cursor/text position one location to the right
@@ -1935,7 +2010,7 @@ Function that moves the cursor/text position one location to the right
 ### _move_up
 
 ```python
-def _move_up(self)
+def _move_up(self) -> None
 ```
 
 Function that moves the cursor/text position one location up
@@ -1949,7 +2024,7 @@ Function that moves the cursor/text position one location up
 ### _move_down
 
 ```python
-def _move_down(self)
+def _move_down(self) -> None
 ```
 
 Function that moves the cursor/text position one location down
@@ -1963,7 +2038,7 @@ Function that moves the cursor/text position one location down
 ### _handle_newline
 
 ```python
-def _handle_newline(self)
+def _handle_newline(self) -> None
 ```
 
 Function that handles recieving newline characters in the text
@@ -1977,7 +2052,7 @@ Function that handles recieving newline characters in the text
 ### _handle_backspace
 
 ```python
-def _handle_backspace(self)
+def _handle_backspace(self) -> None
 ```
 
 Function that handles recieving backspace characters in the text
@@ -1991,7 +2066,7 @@ Function that handles recieving backspace characters in the text
 ### _handle_home
 
 ```python
-def _handle_home(self)
+def _handle_home(self) -> None
 ```
 
 Function that handles recieving a home keypress
@@ -2005,7 +2080,7 @@ Function that handles recieving a home keypress
 ### _handle_end
 
 ```python
-def _handle_end(self)
+def _handle_end(self) -> None
 ```
 
 Function that handles recieving an end keypress
@@ -2019,7 +2094,7 @@ Function that handles recieving an end keypress
 ### _handle_delete
 
 ```python
-def _handle_delete(self)
+def _handle_delete(self) -> None
 ```
 
 Function that handles recieving a delete keypress
@@ -2033,7 +2108,7 @@ Function that handles recieving a delete keypress
 ### _insert_char
 
 ```python
-def _insert_char(self, key_pressed)
+def _insert_char(self, key_pressed: int) -> None
 ```
 
 Function that handles recieving a character
diff --git a/docs/DocstringGenerated/WidgetSet.md b/docs/DocstringGenerated/WidgetSet.md
index 3ae788b..0daf681 100644
--- a/docs/DocstringGenerated/WidgetSet.md
+++ b/docs/DocstringGenerated/WidgetSet.md
@@ -36,6 +36,7 @@ Use PyCUI.apply_widget_set() to set a given widget set for display
  widgets  |  dict of str - py_cui.widgets.Widget | dict of widget in the grid
  keybindings  |  list of py_cui.keybinding.KeyBinding | list of keybindings to check against in the main CUI loop
  height, width  |  int | height of the terminal in characters, width of terminal in characters
+ root  |  py_cui.PyCUI | Main PyCUI object reference
 
 #### Methods
 
@@ -59,7 +60,7 @@ Use PyCUI.apply_widget_set() to set a given widget set for display
 ### __init__
 
 ```python
-def __init__(self, num_rows, num_cols, logger, simulated_terminal=None)
+def __init__(self, num_rows: int, num_cols: int, logger: 'py_cui.debug.PyCUILogger', root:'py_cui.PyCUI', simulated_terminal: Optional[List[int]] =None)
 ```
 
 Constructor for WidgetSet
@@ -73,7 +74,7 @@ Constructor for WidgetSet
 ### set_selected_widget
 
 ```python
-def set_selected_widget(self, widget_id)
+def set_selected_widget(self, widget_id: int) -> None
 ```
 
 Function that sets the selected cell for the CUI
@@ -94,7 +95,7 @@ Function that sets the selected cell for the CUI
 ### get_widgets
 
 ```python
-def get_widgets(self)
+def get_widgets(self) -> Dict[int, Optional['py_cui.widgets.Widget']]
 ```
 
 Function that gets current set of widgets
@@ -115,7 +116,7 @@ Function that gets current set of widgets
 ### add_key_command
 
 ```python
-def add_key_command(self, key, command)
+def add_key_command(self, key: int, command: Callable[[],Any])
 ```
 
 Function that adds a keybinding to the CUI when in overview mode
@@ -137,7 +138,7 @@ Function that adds a keybinding to the CUI when in overview mode
 ### add_scroll_menu
 
 ```python
-def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0)
+def add_scroll_menu(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.ScrollMenu'
 ```
 
 Function that adds a new scroll menu to the CUI grid
@@ -170,7 +171,7 @@ Function that adds a new scroll menu to the CUI grid
 ### add_checkbox_menu
 
 ```python
-def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0, checked_char='X')
+def add_checkbox_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0, checked_char: str='X') -> 'py_cui.widgets.CheckBoxMenu'
 ```
 
 Function that adds a new checkbox menu to the CUI grid
@@ -204,7 +205,7 @@ Function that adds a new checkbox menu to the CUI grid
 ### add_text_box
 
 ```python
-def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '', password = False)
+def add_text_box(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '', password: bool = False) -> 'py_cui.widgets.TextBox'
 ```
 
 Function that adds a new text box to the CUI grid
@@ -239,7 +240,7 @@ Function that adds a new text box to the CUI grid
 ### add_text_block
 
 ```python
-def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '')
+def add_text_block(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '') -> 'py_cui.widgets.ScrollTextBlock'
 ```
 
 Function that adds a new text block to the CUI grid
@@ -273,7 +274,7 @@ Function that adds a new text block to the CUI grid
 ### add_label
 
 ```python
-def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0)
+def add_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.Label'
 ```
 
 Function that adds a new label to the CUI grid
@@ -306,7 +307,7 @@ Function that adds a new label to the CUI grid
 ### add_block_label
 
 ```python
-def add_block_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, center=True)
+def add_block_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, center: bool=True) -> 'py_cui.widgets.BlockLabel'
 ```
 
 Function that adds a new block label to the CUI grid
@@ -340,7 +341,7 @@ Function that adds a new block label to the CUI grid
 ### add_button
 
 ```python
-def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, command=None)
+def add_button(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, command: Optional[Callable[[],Any]]=None) -> 'py_cui.widgets.Button'
 ```
 
 Function that adds a new button to the CUI grid
@@ -374,7 +375,7 @@ Function that adds a new button to the CUI grid
 ### add_slider
 
 ```python
-def add_slider(self, title, row, column, row_span=1
+def add_slider(self, title: str, row: int, column: int, row_span: int=1
 ```
 
 Function that adds a new label to the CUI grid
diff --git a/docs/DocstringGenerated/Widgets.md b/docs/DocstringGenerated/Widgets.md
index 487074c..4899010 100644
--- a/docs/DocstringGenerated/Widgets.md
+++ b/docs/DocstringGenerated/Widgets.md
@@ -1,6 +1,6 @@
 # widgets
 
-Module contatining all core widget classes for py_cui.
+Module containing all core widget classes for py_cui.
 
 
 
@@ -66,6 +66,7 @@ and setting status bar text.
  Method  | Doc
 -----|-----
  add_key_command | Maps a keycode to a function that will be executed when in focus mode
+ add_mouse_command | Maps a keycode to a function that will be executed when in focus mode
  update_key_command | Maps a keycode to a function that will be executed when in focus mode, if key is already mapped
  add_text_color_rule | Forces renderer to draw text using given color if text_condition_function returns True
  get_absolute_start_pos | Gets the absolute position of the widget in characters. Override of base class function
@@ -75,6 +76,7 @@ and setting status bar text.
  set_selectable | Setter for widget selectablility
  is_selectable | Checks if the widget is selectable
  _is_row_col_inside | Checks if a particular row + column is inside the widget area
+ _handle_mouse_press | Base class function that handles all assigned mouse presses.
  _handle_key_press | Base class function that handles all assigned key presses.
  _draw | Base class draw class that checks if renderer is valid.
 
@@ -84,14 +86,14 @@ and setting status bar text.
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, selectable = True)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, selectable: bool = True)
 ```
 
 Initializer for base widget class
 
 
 
-Calss UIElement superclass initialzier, and then assigns widget to grid, along with row/column info
+Class UIElement superclass initializer, and then assigns widget to grid, along with row/column info
 and color rules and key commands
 
 
@@ -101,7 +103,7 @@ and color rules and key commands
 ### add_key_command
 
 ```python
-def add_key_command(self, key, command)
+def add_key_command(self, key: int, command: Callable[[],Any]) -> Any
 ```
 
 Maps a keycode to a function that will be executed when in focus mode
@@ -120,10 +122,38 @@ Maps a keycode to a function that will be executed when in focus mode
 
 
 
+### add_mouse_command
+
+```python
+def add_mouse_command(self, mouse_event: int, command: Callable[[],Any]) -> None
+```
+
+Maps a keycode to a function that will be executed when in focus mode
+
+
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ key  |  py_cui.keys.MOUSE_EVENT | Mouse event code from py_cui.keys
+ command  |  Callable | a non-argument function or lambda function to execute if in focus mode and key is pressed
+
+#### Raises
+
+ Error  | Type  | Doc
+-----|----------|-----
+ Unknown | PyCUIError | If input mouse event code is not valid
+
+
+
+
+
 ### update_key_command
 
 ```python
-def update_key_command(self, key, command)
+def update_key_command(self, key: int, command: Callable[[],Any]) -> Any
 ```
 
 Maps a keycode to a function that will be executed when in focus mode, if key is already mapped
@@ -145,7 +175,7 @@ Maps a keycode to a function that will be executed when in focus mode, if key is
 ### add_text_color_rule
 
 ```python
-def add_text_color_rule(self, regex, color, rule_type, match_type='line', region=[0,1], include_whitespace=False, selected_color=None)
+def add_text_color_rule(self, regex: str, color: int, rule_type: str, match_type: str='line', region: List[int]=[0,1], include_whitespace: bool=False, selected_color=None) -> None
 ```
 
 Forces renderer to draw text using given color if text_condition_function returns True
@@ -171,7 +201,7 @@ Forces renderer to draw text using given color if text_condition_function return
 ### get_absolute_start_pos
 
 ```python
-def get_absolute_start_pos(self)
+def get_absolute_start_pos(self) -> Tuple[int,int]
 ```
 
 Gets the absolute position of the widget in characters. Override of base class function
@@ -192,7 +222,7 @@ Gets the absolute position of the widget in characters. Override of base class f
 ### get_absolute_stop_pos
 
 ```python
-def get_absolute_stop_pos(self)
+def get_absolute_stop_pos(self) -> Tuple[int,int]
 ```
 
 Gets the absolute dimensions of the widget in characters. Override of base class function
@@ -213,7 +243,7 @@ Gets the absolute dimensions of the widget in characters. Override of base class
 ### get_grid_cell
 
 ```python
-def get_grid_cell(self)
+def get_grid_cell(self) -> Tuple[int,int]
 ```
 
 Gets widget row, column in grid
@@ -234,7 +264,7 @@ Gets widget row, column in grid
 ### get_grid_cell_spans
 
 ```python
-def get_grid_cell_spans(self)
+def get_grid_cell_spans(self) -> Tuple[int,int]
 ```
 
 Gets widget row span, column span in grid
@@ -255,7 +285,7 @@ Gets widget row span, column span in grid
 ### set_selectable
 
 ```python
-def set_selectable(self, selectable)
+def set_selectable(self, selectable: bool) -> None
 ```
 
 Setter for widget selectablility
@@ -274,7 +304,7 @@ Widget selectable if true, otherwise not
 ### is_selectable
 
 ```python
-def is_selectable(self)
+def is_selectable(self) -> bool
 ```
 
 Checks if the widget is selectable
@@ -295,7 +325,7 @@ Checks if the widget is selectable
 ### _is_row_col_inside
 
 ```python
-def _is_row_col_inside(self, row, col)
+def _is_row_col_inside(self, row: int, col: int) -> bool
 ```
 
 Checks if a particular row + column is inside the widget area
@@ -319,10 +349,34 @@ Checks if a particular row + column is inside the widget area
 
 
 
+### _handle_mouse_press
+
+```python
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int)
+```
+
+Base class function that handles all assigned mouse presses.
+
+
+
+When overwriting this function, make sure to add a super()._handle_mouse_press(x, y, mouse_event) call,
+as this is required for user defined key command support
+
+
+#### Parameters
+
+ Parameter  | Type  | Doc
+-----|----------|-----
+ key_pressed  |  int | key code of key pressed
+
+
+
+
+
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Base class function that handles all assigned key presses.
@@ -346,7 +400,7 @@ as this is required for user defined key command support
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Base class draw class that checks if renderer is valid.
@@ -395,7 +449,7 @@ Simply displays one centered row of text. Has no unique attributes or methods
 ### __init__
 
 ```python
-def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, pady, logger)
+def __init__(self, id, title: str,  grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger)
 ```
 
 Initalizer for Label widget
@@ -409,7 +463,7 @@ Initalizer for Label widget
 ### toggle_border
 
 ```python
-def toggle_border(self)
+def toggle_border(self) -> None
 ```
 
 Function that gives option to draw border around label
@@ -423,7 +477,7 @@ Function that gives option to draw border around label
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override base draw class.
@@ -471,7 +525,7 @@ A Variation of the label widget that renders a block of text.
 ### __init__
 
 ```python
-def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, pady, center, logger)
+def __init__(self, id, title: str,  grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, center, logger)
 ```
 
 Initializer for blocklabel widget
@@ -485,7 +539,7 @@ Initializer for blocklabel widget
 ### set_title
 
 ```python
-def set_title(self, title)
+def set_title(self, title: str) -> None
 ```
 
 Override of base class, splits title into lines for rendering line by line.
@@ -506,7 +560,7 @@ Override of base class, splits title into lines for rendering line by line.
 ### toggle_border
 
 ```python
-def toggle_border(self)
+def toggle_border(self) -> None
 ```
 
 Function that gives option to draw border around label
@@ -520,7 +574,7 @@ Function that gives option to draw border around label
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override base draw class.
@@ -560,7 +614,7 @@ A scroll menu widget.
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger: 'py_cui.debug.PyCUILogger')
 ```
 
 Initializer for scroll menu. calls superclass initializers and sets help text
@@ -574,7 +628,7 @@ Initializer for scroll menu. calls superclass initializers and sets help text
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int)
 ```
 
 Override of base class function, handles mouse press in menu
@@ -595,7 +649,7 @@ Override of base class function, handles mouse press in menu
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override base class function.
@@ -618,7 +672,7 @@ UP_ARROW scrolls up, DOWN_ARROW scrolls down.
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Overrides base class draw function
@@ -664,7 +718,7 @@ Extension of ScrollMenu that allows for multiple items to be selected at once.
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, checked_char)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, checked_char: str)
 ```
 
 Initializer for CheckBoxMenu Widget
@@ -678,7 +732,7 @@ Initializer for CheckBoxMenu Widget
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Override of base class function, handles mouse press in menu
@@ -699,7 +753,7 @@ Override of base class function, handles mouse press in menu
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of key presses.
@@ -723,7 +777,7 @@ Adds Enter command to toggle selection
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Overrides base class draw function
@@ -769,7 +823,7 @@ Allows for running a command function on Enter
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, command)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, command: Optional[Callable[[],Any]])
 ```
 
 Initializer for Button Widget
@@ -783,7 +837,7 @@ Initializer for Button Widget
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class, adds ENTER listener that runs the button's command
@@ -804,7 +858,7 @@ Override of base class, adds ENTER listener that runs the button's command
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class draw function
@@ -843,7 +897,7 @@ Widget for entering small single lines of text
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, initial_text, password)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, initial_text: str, password: bool)
 ```
 
 Initializer for TextBox widget. Uses TextBoxImplementation as base
@@ -857,7 +911,7 @@ Initializer for TextBox widget. Uses TextBoxImplementation as base
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Need to update all cursor positions on resize
@@ -871,7 +925,7 @@ Need to update all cursor positions on resize
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Override of base class function, handles mouse press in menu
@@ -892,7 +946,7 @@ Override of base class function, handles mouse press in menu
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base handle key press function
@@ -913,7 +967,7 @@ Override of base handle key press function
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base draw function
@@ -952,7 +1006,7 @@ Widget for editing large multi-line blocks of text
 ### __init__
 
 ```python
-def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, initial_text)
+def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, initial_text: str)
 ```
 
 Initializer for TextBlock Widget. Uses TextBlockImplementation as base
@@ -966,7 +1020,7 @@ Initializer for TextBlock Widget. Uses TextBlockImplementation as base
 ### update_height_width
 
 ```python
-def update_height_width(self)
+def update_height_width(self) -> None
 ```
 
 Function that updates the position of the text and cursor on resize
@@ -980,7 +1034,7 @@ Function that updates the position of the text and cursor on resize
 ### _handle_mouse_press
 
 ```python
-def _handle_mouse_press(self, x, y)
+def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None
 ```
 
 Override of base class function, handles mouse press in menu
@@ -1001,7 +1055,7 @@ Override of base class function, handles mouse press in menu
 ### _handle_key_press
 
 ```python
-def _handle_key_press(self, key_pressed)
+def _handle_key_press(self, key_pressed: int) -> None
 ```
 
 Override of base class handle key press function
@@ -1022,7 +1076,7 @@ Override of base class handle key press function
 ### _draw
 
 ```python
-def _draw(self)
+def _draw(self) -> None
 ```
 
 Override of base class draw function
diff --git a/docs/developers.md b/docs/developers.md
index 4948f63..a896019 100644
--- a/docs/developers.md
+++ b/docs/developers.md
@@ -86,7 +86,7 @@ class MenuImplementation(UIImplementation):
         if self._selected_item > 0:
             self._selected_item = self._selected_item - 1
 
-        self._logger.debug('Scrolling up to item {}'.format(self._selected_item))
+        self._logger.debug(f'Scrolling up to item {self._selected_item}')
 
     def _scroll_down(self, viewport_height):
         if self._selected_item < len(self._view_items) - 1:
@@ -94,22 +94,22 @@ class MenuImplementation(UIImplementation):
         if self._selected_item > self._top_view + viewport_height:
             self._top_view = self._top_view + 1
 
-        self._logger.debug('Scrolling down to item {}'.format(self._selected_item))
+        self._logger.debug(f'Scrolling down to item {self._selected_item}')
 
     def add_item(self, item_text):
-        self._logger.debug('Adding item {} to menu'.format(item_text))
+        self._logger.debug(f'Adding item {item_text} to menu')
         self._view_items.append(item_text)
 
 
     def add_item_list(self, item_list):
-        self._logger.debug('Adding item list {} to menu'.format(str(item_list)))
+        self._logger.debug(f'Adding item list {str(item_list)} to menu')
         for item in item_list:
             self.add_item(item)
 
     def remove_selected_item(self):
         if len(self._view_items) == 0:
             return
-        self._logger.debug('Removing {}'.format(self._view_items[self._selected_item]))
+        self._logger.debug(f'Removing {self._view_items[self._selected_item]}')
         del self._view_items[self._selected_item]
         if self._selected_item >= len(self._view_items):
             self._selected_item = self._selected_item - 1
@@ -205,7 +205,7 @@ Finally, add a function to the `PyCUI` class in `__init__.py` that will add the
 ```Python
 def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0):
 
-    id = 'Widget{}'.format(len(self.get_widgets().keys()))
+    id = f'Widget{len(self.get_widgets().keys())}'
     new_scroll_menu = widgets.ScrollMenu(   id, 
                                             title, 
                                             self._grid, 
@@ -219,7 +219,7 @@ def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, pad
     self.get_widgets()[id] = new_scroll_menu
     if self._selected_widget is None:
         self.set_selected_widget(id)
-    self._logger.debug('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_scroll_menu))))
+    self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_scroll_menu))}'
     return new_scroll_menu
 ```
 The function must:
@@ -247,4 +247,4 @@ This documentation section is incomplete. Feel free to [expand me](https://githu
 
 ### Working on color rules
 
-This documentation section is incomplete. Feel free to [expand me](https://github.com/jwlodek/py_cui/pulls).
\ No newline at end of file
+This documentation section is incomplete. Feel free to [expand me](https://github.com/jwlodek/py_cui/pulls).
diff --git a/docs/examples.md b/docs/examples.md
index f18c85b..0666ed3 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -51,7 +51,7 @@ class SimpleTodoList:
     def add_item(self):
         """ Add a todo item """
 
-        self.todo_scroll_cell.add_item('{}'.format(self.new_todo_textbox.get()))
+        self.todo_scroll_cell.add_item(f'{self.new_todo_textbox.get()}')
         self.new_todo_textbox.clear()
 
     def mark_as_in_progress(self):
diff --git a/docs/index.md b/docs/index.md
index 6faa962..2ae189f 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -23,7 +23,7 @@
 
 # py_cui
 
-`py_cui` is a python library meant to simplify writing command line user interfaces in python. It relies on `curses`, which is traditionally unix-specific, however, you may use the [windows-curses](https://github.com/zephyrproject-rtos/windows-curses) module to run `py_cui` on windows.
+`py_cui` is a python library meant to simplify writing console user interfaces in python. It relies on `curses`, which is traditionally unix-specific, however, you may use the [windows-curses](https://github.com/zephyrproject-rtos/windows-curses) module to run `py_cui` on windows.
 
 The main advantage `py_cui` has over typical text-based user interface builders is that it relies on widgets and a grid layout manager like most traditional graphical user interfaces. You may define a grid size, and then drop predefined widgets onto it into specific grid locations. Widgets can also be stretched accross multiple grid rows and columns. If you've ever made a Tkinter GUI, you will feel right at home.
 
diff --git a/docs/popups.md b/docs/popups.md
index f3b2938..e9951a8 100644
--- a/docs/popups.md
+++ b/docs/popups.md
@@ -6,13 +6,14 @@ This page contains information regarding all popups supported by `py_cui`. Pleas
 
 **Spawn Command**
 ```
-show_message_popup(title, text)
+show_message_popup(title, text, color = WHITE_ON_BLACK)
 show_warning_popup(title, text)
 show_error_popup(title, text)
 ```
 **Usage**
 
-Used to show a simple message, with a different color depending on warning level.
+`show_message_popup` takes an optional `color` argument which defaults to standard WHITE_ON_BLACK.
+`show_warning_popup` and `show_error_popup` are shorthand for respectively yellow and red colors.
 
 **Keys**
 
diff --git a/docs/writing.md b/docs/writing.md
index 0fdaf3a..0a1f0dd 100644
--- a/docs/writing.md
+++ b/docs/writing.md
@@ -128,7 +128,7 @@ class SimpleTodoList:
     def add_item(self):
         """ Add a todo item """
 
-        self.todo_scroll_cell.add_item('{}'.format(self.new_todo_textbox.get()))
+        self.todo_scroll_cell.add_item(f'{self.new_todo_textbox.get()}')
         self.new_todo_textbox.clear()
 
 
@@ -249,4 +249,4 @@ wrapper = MultiWindowDemo(root)
 root.start()
 ```
 
-In addition, it may be worthwhile to create additional wrapper classes for the individual screens in order to better separate the logic for each. An example of this approach would be the [`ScreenManager`](https://github.com/jwlodek/pyautogit/blob/master/pyautogit/screen_manager.py) and various [sub-classes](https://github.com/jwlodek/pyautogit/blob/master/pyautogit/settings_screen.py) for `pyautogit`.
\ No newline at end of file
+In addition, it may be worthwhile to create additional wrapper classes for the individual screens in order to better separate the logic for each. An example of this approach would be the [`ScreenManager`](https://github.com/jwlodek/pyautogit/blob/master/pyautogit/screen_manager.py) and various [sub-classes](https://github.com/jwlodek/pyautogit/blob/master/pyautogit/settings_screen.py) for `pyautogit`.
diff --git a/examples/autogit.py b/examples/autogit.py
index a4eafa6..bf325fa 100644
--- a/examples/autogit.py
+++ b/examples/autogit.py
@@ -33,11 +33,11 @@ def __init__(self, root: py_cui.PyCUI, dir):
         res = proc.returncode
         if res != 0:
             print(res)
-            print('ERROR - fatal, {} is not a git repository.'.format(self.dir))
+            print(f'ERROR - fatal, {self.dir} is not a git repository.')
             exit()
 
         # Set title
-        self.root.set_title('Autogit v{} - {}'.format(__version__, os.path.basename(self.dir)))
+        self.root.set_title(f'Autogit v{__version__} - {os.path.basename(self.dir)}')
 
         # Keybindings when in overview mode, and set info bar
         self.root.add_key_command(py_cui.keys.KEY_R_LOWER, self.refresh_git_status)
@@ -150,7 +150,7 @@ def show_git_commit_diff(self):
             proc = Popen(['git', 'diff', commit_val], stdout=PIPE, stderr=PIPE)
             out, _ = proc.communicate()
             out = out.decode()
-            self.diff_text_block.set_title('Git Diff for {}'.format(commit_val))
+            self.diff_text_block.set_title(f'Git Diff for {commit_val}')
             self.diff_text_block.set_text(out)
         except:
             self.root.show_warning_popup('Git Failed', 'Unable to read commit diff information')
@@ -205,11 +205,11 @@ def create_new_branch(self):
             _, err = proc.communicate()
             res = proc.returncode
             if res != 0:
-                self.root.show_error_popup('Create Branch Failed Failed', '{}'.format(err))
+                self.root.show_error_popup('Create Branch Failed Failed', f'{err}')
                 return
             self.refresh_git_status(preserve_selected=True)
             self.new_branch_textbox.clear()
-            self.root.show_message_popup('Success', 'Checked out branch {}'.format(new_branch_name))
+            self.root.show_message_popup('Success', f'Checked out branch {new_branch_name}')
         except:
             self.root.show_warning_popup('Git Failed', 'Unable to checkout branch, please check git installation')
 
@@ -228,11 +228,11 @@ def commit_changes(self, commit):
             _, err = proc.communicate()
             res = proc.returncode
             if res != 0:
-                self.root.show_error_popup('Create Branch Failed Failed', '{}'.format(err))
+                self.root.show_error_popup('Create Branch Failed Failed', f'{err}')
                 return
             self.refresh_git_status(preserve_selected=True)
             self.commit_message_box.clear()
-            self.root.show_message_popup('Success', 'Commited: {}'.format(message))
+            self.root.show_message_popup('Success', f'Commited: {message}')
         else:
             self.root.show_message_popup('Cancelled', 'Commit Operation cancelled')
 
@@ -244,10 +244,10 @@ def checkout_branch(self):
             out, _ = proc.communicate()
             res = proc.returncode
             if res != 0:
-                self.root.show_error_popup('Checkout Failed', '{}'.format(out))
+                self.root.show_error_popup('Checkout Failed', f'{out}')
                 return
             self.refresh_git_status(preserve_selected=True)
-            self.root.show_message_popup('Success', 'Checked out branch {}'.format(target))
+            self.root.show_message_popup('Success', f'Checked out branch {target}')
         except:
             self.root.show_warning_popup('Git Failed', 'Unable to checkout branch, please check git installation')
 
@@ -264,7 +264,7 @@ def revert_changes(self):
     def open_git_diff(self):
 
         target = self.add_files_menu.get()[3:]
-        self.diff_text_block.title = '{} File Diff'.format(target)
+        self.diff_text_block.title = f'{target} File Diff'
         proc = Popen(['git', 'diff', target], stdout=PIPE, stderr=PIPE)
         out, _ = proc.communicate()
         out = out.decode()
@@ -338,10 +338,10 @@ def fetch_branch(self):
             out, _ = proc.communicate()
             res = proc.returncode
             if res != 0:
-                self.root.show_error_popup('Checkout Failed', '{}'.format(out))
+                self.root.show_error_popup('Checkout Failed', f'{out}')
                 return
             self.refresh_git_status(preserve_selected=True)
-            self.root.show_message_popup('Success', 'Checked out branch {}'.format(target))
+            self.root.show_message_popup('Success', f'Checked out branch {target}')
         except FileNotFoundError:
             self.root.show_warning_popup('Git Failed', 'Unable to checkout branch, please check git installation')
 
@@ -356,10 +356,10 @@ def parse_args():
     if 'directory' not in args.keys():
         return '.' 
     elif not os.path.exists(args['directory']):
-        print('ERROR - {} path does not exist'.format(args['directory']))
+        print(f'ERROR - {args["directory"]} path does not exist')
         exit()
     elif not os.path.isdir(args['directory']):
-        print('ERROR - {} is not a directory'.format(args['directory']))
+        print(f'ERROR - {args["directory"]} is not a directory')
         exit()
     return args['directory']
 
diff --git a/examples/mouse.py b/examples/mouse.py
new file mode 100644
index 0000000..3b4ed57
--- /dev/null
+++ b/examples/mouse.py
@@ -0,0 +1,69 @@
+import py_cui
+import py_cui.keys as KEYS
+
+
+class MouseApp:
+
+    def __init__(self, root: py_cui.PyCUI):
+
+        # Initialize our two widgets, a button and a mouse press log
+        self.root = root
+        self.button_presser = self.root.add_button('Press Me!', 0, 0)
+        self.mouse_press_log = self.root.add_text_block('Mouse Presses', 1, 0, row_span=2)
+
+        # This demonstrates how you can get mouse press coordinates to a mouse press event function
+        # You may pass in either a no-parameter function or one with two parameters as the second argument
+        # to add_mouse_command. If it is detected as one with two parameters, the function will be ran with
+        # the x, y coordinates of the press in terminal characters as the input.
+        self.button_presser.add_mouse_command(KEYS.LEFT_MOUSE_CLICK, self.print_left_press_with_coords)
+
+        # Demonstration of how to add mouse commands for remaining left click events
+        self.button_presser.add_mouse_command(KEYS.LEFT_MOUSE_DBL_CLICK, lambda: self.mouse_press_log.set_text('Left Double\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.LEFT_MOUSE_PRESSED, lambda: self.mouse_press_log.set_text('Left Pressed\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.LEFT_MOUSE_RELEASED, lambda: self.mouse_press_log.set_text('Left Released\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.LEFT_MOUSE_TRPL_CLICK, lambda: self.mouse_press_log.set_text('Left Triple\n' + self.mouse_press_log.get()))
+
+        # Demonstration of how to add mouse commands for middle click events
+        self.button_presser.add_mouse_command(KEYS.MIDDLE_MOUSE_CLICK, lambda: self.mouse_press_log.set_text('Middle Single\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.MIDDLE_MOUSE_DBL_CLICK, lambda: self.mouse_press_log.set_text('Middle Double\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.MIDDLE_MOUSE_PRESSED, lambda: self.mouse_press_log.set_text('Middle Pressed\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.MIDDLE_MOUSE_RELEASED, lambda: self.mouse_press_log.set_text('Middle Released\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.MIDDLE_MOUSE_TRPL_CLICK, lambda: self.mouse_press_log.set_text('Middle Triple\n' + self.mouse_press_log.get()))
+
+        # Demonstration of how to add mouse commands for right click events
+        self.button_presser.add_mouse_command(KEYS.RIGHT_MOUSE_CLICK, lambda: self.mouse_press_log.set_text('Right Single\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.RIGHT_MOUSE_DBL_CLICK, lambda: self.mouse_press_log.set_text('Right Double\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.RIGHT_MOUSE_PRESSED, lambda: self.mouse_press_log.set_text('Right Pressed\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.RIGHT_MOUSE_RELEASED, lambda: self.mouse_press_log.set_text('Right Released\n' + self.mouse_press_log.get()))
+        self.button_presser.add_mouse_command(KEYS.RIGHT_MOUSE_TRPL_CLICK, lambda: self.mouse_press_log.set_text('Right Triple\n' + self.mouse_press_log.get()))
+
+        # Two scroll menus demonstrating how to handle drag and drop functionality with py_cui
+        self.drag_from = self.root.add_scroll_menu('Drag From', 0, 1, row_span=3)
+        self.drag_to = self.root.add_scroll_menu('Drag To', 0, 2, row_span=3)
+
+        self.drag_from.add_mouse_command(KEYS.LEFT_MOUSE_PRESSED, self.drag)
+        self.drag_to.add_mouse_command(KEYS.LEFT_MOUSE_RELEASED, self.drop)
+
+        self.drag_from.add_item_list(['Drag Me', 'Drop Me Too'])
+
+        self.current_clipboard_elem = None
+
+
+    def drag(self):
+        self.current_clipboard_elem = self.drag_from.get()
+
+
+    def drop(self):
+        self.drag_to.add_item(self.current_clipboard_elem)
+        self.drag_from.remove_item(self.current_clipboard_elem)
+        self.current_clipboard_elem = None
+
+
+    def print_left_press_with_coords(self, x, y):
+
+        self.mouse_press_log.set_text(f'Left Single w/ coords {x}, {y}\n' + self.mouse_press_log.get())
+
+
+root = py_cui.PyCUI(3, 3)
+MouseApp(root)
+root.start()
\ No newline at end of file
diff --git a/examples/simple_todo_list.py b/examples/simple_todo_list.py
index b6c2f51..11be298 100644
--- a/examples/simple_todo_list.py
+++ b/examples/simple_todo_list.py
@@ -81,7 +81,7 @@ def add_item(self):
         """Add a todo item
         """
 
-        self.todo_scroll_cell.add_item('{}'.format(self.new_todo_textbox.get()))
+        self.todo_scroll_cell.add_item(f'{self.new_todo_textbox.get()}')
 
 
     def remove_item(self):
@@ -141,5 +141,7 @@ def save_todo_file(self):
 root = py_cui.PyCUI(8, 6)
 root.set_title('CUI TODO List')
 #root.enable_logging(logging_level=logging.DEBUG)
+root.enable_logging(logging_level=logging.ERROR)
+#root.toggle_live_debug_mode()
 s = SimpleTodoList(root)
 root.start()
diff --git a/examples/snano.py b/examples/snano.py
index 0abb5a3..02636ae 100644
--- a/examples/snano.py
+++ b/examples/snano.py
@@ -66,10 +66,10 @@ def open_new_directory(self):
         if len(target) == 0:
             target = '.'
         elif not os.path.exists(target):
-            self.root.show_error_popup('Does not exist', 'ERROR - {} path does not exist'.format(target))
+            self.root.show_error_popup('Does not exist', f'ERROR - {target} path does not exist')
             return
         elif not os.path.isdir(target):
-            self.root.show_error_popup('Not a Dir', 'ERROR - {} is not a directory'.format(target))
+            self.root.show_error_popup('Not a Dir', f'ERROR - {target} is not a directory')
             return
         target = os.path.abspath(target)
         self.current_dir_box.set_text(target)
@@ -128,7 +128,7 @@ def save_opened_file(self):
             fp = open(os.path.join(self.dir, self.edit_text_block.get_title()), 'w')
             fp.write(self.edit_text_block.get())
             fp.close()
-            self.root.show_message_popup('Saved', 'Your file has been saved as {}'.format(self.edit_text_block.get_title()))
+            self.root.show_message_popup('Saved', f'Your file has been saved as {self.edit_text_block.get_title()}')
         else:
             self.root.show_error_popup('No File Opened', 'Please open a file before saving it.')
 
@@ -156,10 +156,10 @@ def parse_args():
     if 'directory' not in args.keys():
         return '.' 
     elif not os.path.exists(args['directory']):
-        print('ERROR - {} path does not exist'.format(args['directory']))
+        print(f'ERROR - {args["directory"]} path does not exist')
         exit()
     elif not os.path.isdir(args['directory']):
-        print('ERROR - {} is not a directory'.format(args['directory']))
+        print(f'ERROR - {args["directory"]} is not a directory')
         exit()
     return args['directory']
 
@@ -169,7 +169,7 @@ def parse_args():
 
 # Initialize the PyCUI object, and set the title
 root = py_cui.PyCUI(7, 8)
-root.set_title('Super Nano v{}'.format(__version__))
+root.set_title(f'Super Nano v{__version__}')
 
 # Create the wrapper instance object.
 frame = SuperNano(root, dir)
diff --git a/py_cui/__init__.py b/py_cui/__init__.py
index 6ff77dd..66c585d 100644
--- a/py_cui/__init__.py
+++ b/py_cui/__init__.py
@@ -22,7 +22,7 @@
 # there is a open source windows-curses module that adds curses support
 # for python on windows
 import curses
-
+from typing import Any, Callable, List, Dict, Optional, Tuple
 
 # py_cui imports
 import py_cui
@@ -38,12 +38,11 @@
 import py_cui.errors
 from py_cui.colors import *
 
-
 # Version number
 __version__ = '0.1.4'
 
 
-def fit_text(width, text, center=False):
+def fit_text(width: int, text: str, center: bool = False) -> str:
     """Fits text to screen size
 
     Helper function to fit text within a given width. Used to fix issue with status/title bar text
@@ -108,12 +107,13 @@ class PyCUI:
         Dimensions for an alternative simulated terminal (used for testing)
     """
 
-    def __init__(self, num_rows, num_cols, auto_focus_buttons=True,
-                 exit_key=py_cui.keys.KEY_Q_LOWER, simulated_terminal=None):
-        """Constructor for PyCUI class
+    def __init__(self, num_rows: int, num_cols: int, auto_focus_buttons: bool=True,
+                 exit_key=py_cui.keys.KEY_Q_LOWER, simulated_terminal: List[int]=None):
+        """Initializer for PyCUI class
         """
 
         self._title = 'PyCUI Window'
+        
         # When this is not set, the escape character delay
         # is too long for exiting focus mode
         os.environ.setdefault('ESCDELAY', '25')
@@ -127,23 +127,29 @@ def __init__(self, num_rows, num_cols, auto_focus_buttons=True,
             height  = term_size.lines
             width   = term_size.columns
         else:
-            height  = simulated_terminal[0]
-            width   = simulated_terminal[1]
+            height = self._simulated_terminal[0]
+            width  = self._simulated_terminal[1]
+
+        # Add status and title bar
+        self.title_bar = py_cui.statusbar.StatusBar(self._title, BLACK_ON_WHITE, root=self, is_title_bar=True)
+        exit_key_char = py_cui.keys.get_char_from_ascii(exit_key)
+
+        if exit_key_char:
+            self._init_status_bar_text  = f'Press - {exit_key_char} - to exit. Arrow ' \
+                                        'Keys to move between widgets. Enter to ' \
+                                        'enter focus mode.'
+        else:
+            self._init_status_bar_text  = 'Press arrow Keys to move between widgets. ' \
+                                        'Enter to enter focus mode.' \
+
+        self.status_bar = py_cui.statusbar.StatusBar(self._init_status_bar_text,
+                                                     BLACK_ON_WHITE, root=self)
 
         # Init terminal height width. Subtract 4 from height
         # for title/status bar and padding
         self._height                = height
         self._width                 = width
-        self._height                = self._height - 4
-
-        # Add status and title bar
-        self.title_bar = py_cui.statusbar.StatusBar(self._title, BLACK_ON_WHITE)
-        exit_key_char = py_cui.keys.get_char_from_ascii(exit_key)
-        self._init_status_bar_text  = 'Press - {} - to exit. Arrow Keys to move ' \
-                                       'between widgets. Enter to enter focus ' \
-                                       'mode.'.format(exit_key_char)
-        self.status_bar = py_cui.statusbar.StatusBar(self._init_status_bar_text,
-                                                     BLACK_ON_WHITE)
+        self._height                = self._height - self.title_bar.get_height() - self.status_bar.get_height() - 2
 
         # Logging object initialization for py_cui
         self._logger = py_cui.debug._initialize_logger(self,
@@ -151,35 +157,36 @@ def __init__(self, num_rows, num_cols, auto_focus_buttons=True,
 
         # Initialize grid, renderer, and widget dict
         self._grid                  = py_cui.grid.Grid(num_rows, num_cols, self._height, self._width, self._logger)
-        self._renderer              = None
-        self._border_characters     = None
-        self._stdscr                = None
-        self._widgets               = {}
+        self._stdscr: Any              = None
         self._refresh_timeout       = -1
+        self._border_characters: Optional[Dict[str,str]] = None
+        self._widgets: Dict[int,Optional['py_cui.widgets.Widget']] = {}
+        self._renderer: Optional['py_cui.renderer.Renderer'] = None
 
         # Variables for determining selected widget/focus mode
-        self._selected_widget       = None
+        self._selected_widget: Optional[int] = None
         self._in_focused_mode       = False
-        self._popup                 = None
+        self._popup: Any = None
         self._auto_focus_buttons    = auto_focus_buttons
 
         # CUI blocks when loading popup is open
         self._loading               = False
         self._stopped               = False
-        self._post_loading_callback = None
-        self._on_draw_update_func   = None
+        self._post_loading_callback: Optional[Callable[[],Any]] = None
+        self._on_draw_update_func: Optional[Callable[[],Any]]   = None
 
         # Top level keybindings. Exit key is 'q' by default
-        self._keybindings  = {}
+        self._keybindings: Dict[int,Callable[[],Any]]  = {}
         self._exit_key     = exit_key
         self._forward_cycle_key = py_cui.keys.KEY_CTRL_LEFT
         self._reverse_cycle_key = py_cui.keys.KEY_CTRL_RIGHT
+        self._toggle_live_debug_key: Optional[int] = None
 
         # Callback to fire when CUI is stopped.
-        self._on_stop = None
+        self._on_stop: Optional[Callable[[],Any]] = None
 
 
-    def set_refresh_timeout(self, timeout):
+    def set_refresh_timeout(self, timeout: int):
         """Sets the CUI auto-refresh timeout to a number of seconds.
 
         Parameters
@@ -192,7 +199,7 @@ def set_refresh_timeout(self, timeout):
         self._refresh_timeout = int(timeout * 1000)
 
 
-    def set_on_draw_update_func(self, update_function):
+    def set_on_draw_update_func(self, update_function: Callable[[],Any]):
         """Adds a function that is fired during each draw call of the CUI
 
         Parameters
@@ -204,7 +211,7 @@ def set_on_draw_update_func(self, update_function):
         self._on_draw_update_func = update_function
 
 
-    def set_widget_cycle_key(self, forward_cycle_key=None, reverse_cycle_key=None):
+    def set_widget_cycle_key(self, forward_cycle_key: int=None, reverse_cycle_key: int=None) -> None: 
         """Assigns a key for automatically cycling through widgets in both focus and overview modes
 
         Parameters
@@ -219,7 +226,11 @@ def set_widget_cycle_key(self, forward_cycle_key=None, reverse_cycle_key=None):
             self._reverse_cycle_key = reverse_cycle_key
 
 
-    def enable_logging(self, log_file_path='py_cui_log.txt', logging_level = logging.DEBUG):
+    def set_toggle_live_debug_key(self, toggle_debug_key: int) -> None:
+        self._toggle_live_debug_key = toggle_debug_key
+
+
+    def enable_logging(self, log_file_path: str='py_cui.log', logging_level = logging.DEBUG, live_debug_key: int = py_cui.keys.KEY_CTRL_D) -> None:
         """Function enables logging for py_cui library
 
         Parameters
@@ -233,11 +244,12 @@ def enable_logging(self, log_file_path='py_cui_log.txt', logging_level = logging
         try:
             py_cui.debug._enable_logging(self._logger, filename=log_file_path, logging_level=logging_level)
             self._logger.info('Initialized logger')
+            self._toggle_live_debug_key = live_debug_key
         except PermissionError as e:
-            print('Failed to initialize logger: {}'.format(str(e)))
+            print(f'Failed to initialize logger: {str(e)}')
 
 
-    def apply_widget_set(self, new_widget_set):
+    def apply_widget_set(self, new_widget_set: py_cui.widget_set.WidgetSet) -> None:
         """Function that replaces all widgets in a py_cui with those of a different widget set
 
         Parameters
@@ -257,21 +269,7 @@ def apply_widget_set(self, new_widget_set):
             self._grid         = new_widget_set._grid
             self._keybindings  = new_widget_set._keybindings
 
-            if self._simulated_terminal is None:
-                if self._stdscr is None:
-                    term_size = shutil.get_terminal_size()
-                    height  = term_size.lines
-                    width   = term_size.columns
-                else:
-                    # Use curses termsize when possible to fix resize bug on windows.
-                    height, width = self._stdscr.getmaxyx()
-            else:
-                height  = self._simulated_terminal[0]
-                width   = self._simulated_terminal[1]
-
-            height  = height - 4
-
-            self._refresh_height_width(height, width)
+            self._refresh_height_width()
             if self._stdscr is not None:
                 self._initialize_widget_renderer()
             self._selected_widget = new_widget_set._selected_widget
@@ -279,7 +277,7 @@ def apply_widget_set(self, new_widget_set):
             raise TypeError('Argument must be of type py_cui.widget_set.WidgetSet')
 
 
-    def create_new_widget_set(self, num_rows, num_cols):
+    def create_new_widget_set(self, num_rows: int, num_cols: int) -> 'py_cui.widget_set.WidgetSet':
         """Function that is used to create additional widget sets
 
         Use this function instead of directly creating widget set object instances, to allow
@@ -299,7 +297,7 @@ def create_new_widget_set(self, num_rows, num_cols):
         """
 
         # Use current logging object and simulated terminal for sub-widget sets
-        return py_cui.widget_set.WidgetSet(num_rows, num_cols, self._logger, 
+        return py_cui.widget_set.WidgetSet(num_rows, num_cols, self._logger, root=self,
                                             simulated_terminal=self._simulated_terminal)
 
 
@@ -309,15 +307,16 @@ def create_new_widget_set(self, num_rows, num_cols):
     # ----------------------------------------------#
 
 
-    def start(self):
+    def start(self) -> None:
         """Function that starts the CUI
         """
 
-        self._logger.info('Starting {} CUI'.format(self._title))
+        self._logger.info(f'Starting {self._title} CUI')
+        self._stopped = False
         curses.wrapper(self._draw)
 
 
-    def stop(self):
+    def stop(self) -> None:
         """Function that stops the CUI, and fires the callback function.
 
         Callback must be a no arg method
@@ -327,7 +326,7 @@ def stop(self):
         self._stopped = True
 
 
-    def run_on_exit(self, command):
+    def run_on_exit(self, command: Callable[[],Any]):
         """Sets callback function on CUI exit. Must be a no-argument function or lambda function
 
         Parameters
@@ -339,7 +338,7 @@ def run_on_exit(self, command):
         self._on_stop = command
 
 
-    def set_title(self, title):
+    def set_title(self, title: str) -> None:
         """Sets the title bar text
 
         Parameters
@@ -351,7 +350,7 @@ def set_title(self, title):
         self._title = title
 
 
-    def set_status_bar_text(self, text):
+    def set_status_bar_text(self, text: str) -> None:
         """Sets the status bar text when in overview mode
 
         Parameters
@@ -364,32 +363,41 @@ def set_status_bar_text(self, text):
         self.status_bar.set_text(text)
 
 
-    def _initialize_colors(self):
+    def _initialize_colors(self) -> None:
         """Function for initialzing curses colors. Called when CUI is first created.
         """
 
         # Start colors in curses.
         # For each color pair in color map, initialize color combination.
         curses.start_color()
-        curses.init_color(curses.COLOR_BLUE, 0, 0, 500)
         for color_pair in py_cui.colors._COLOR_MAP.keys():
             fg_color, bg_color = py_cui.colors._COLOR_MAP[color_pair]
             curses.init_pair(color_pair, fg_color, bg_color)
 
 
-    def _initialize_widget_renderer(self):
+    def _initialize_widget_renderer(self) -> None:
         """Function that creates the renderer object that will draw each widget
         """
 
         if self._renderer is None:
             self._renderer = py_cui.renderer.Renderer(self, self._stdscr, self._logger)
-        for widget_id in self._widgets.keys():
-            self._widgets[widget_id]._assign_renderer(self._renderer)
-        if self._popup is not None:
-            self._popup._assign_renderer(self._renderer)
+        for widget_id in self.get_widgets().keys():
+            widget = self.get_widgets()[widget_id]
+            if widget is not None:
+                try:
+                    widget._assign_renderer(self._renderer)
+                except py_cui.errors.PyCUIError:
+                    self._logger.debug(f'Renderer already assigned for widget {self.get_widgets()[widget_id]}')
+        try:
+            if self._popup is not None:
+                self._popup._assign_renderer(self._renderer)
+            if self._logger is not None:
+                self._logger._live_debug_element._assign_renderer(self._renderer)
+        except py_cui.errors.PyCUIError:
+            self._logger.debug('Renderer already assigned to popup or live-debug elements')
 
 
-    def toggle_unicode_borders(self):
+    def toggle_unicode_borders(self) -> None:
         """Function for toggling unicode based border rendering
         """
 
@@ -399,7 +407,7 @@ def toggle_unicode_borders(self):
             self.set_widget_border_characters('+', '+', '+', '+', '-', '|')
 
 
-    def set_widget_border_characters(self, upper_left_corner, upper_right_corner, lower_left_corner, lower_right_corner, horizontal, vertical):
+    def set_widget_border_characters(self, upper_left_corner: str, upper_right_corner: str, lower_left_corner: str, lower_right_corner: str, horizontal: str, vertical: str) -> None:
         """Function that can be used to set arbitrary border characters for drawing widget borders by renderer.
 
         Parameters
@@ -426,15 +434,15 @@ def set_widget_border_characters(self, upper_left_corner, upper_right_corner, lo
             'HORIZONTAL': horizontal,
             'VERTICAL': vertical
         }
-        self._logger.info('Set border_characters to {}'.format(self._border_characters))
+        self._logger.debug(f'Set border_characters to {self._border_characters}')
 
 
-    def get_widgets(self):
+    def get_widgets(self) -> Dict[int,Optional['py_cui.widgets.Widget']]:  
         """Function that gets current set of widgets
 
         Returns
         -------
-        widgets : dict of str -> widget
+        widgets : dict of int -> widget 
             dictionary mapping widget IDs to object instances
         """
 
@@ -443,7 +451,7 @@ def get_widgets(self):
     # Widget add functions. Each of these adds a particular type of widget
     # to the grid in a specified location.
 
-    def add_scroll_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0) -> py_cui.widgets.ScrollMenu:
+    def add_scroll_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0) -> 'py_cui.widgets.ScrollMenu':
         """Function that adds a new scroll menu to the CUI grid
 
         Parameters
@@ -469,7 +477,7 @@ def add_scroll_menu(self, title, row, column, row_span=1, column_span=1, padx=1,
             A reference to the created scroll menu object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_scroll_menu = py_cui.widgets.ScrollMenu(id,
                                                     title,
                                                     self._grid,
@@ -480,15 +488,16 @@ def add_scroll_menu(self, title, row, column, row_span=1, column_span=1, padx=1,
                                                     padx,
                                                     pady,
                                                     self._logger)
-        new_scroll_menu._assign_renderer(self._renderer)
-        self._widgets[id]  = new_scroll_menu
+        if self._renderer is not None:
+            new_scroll_menu._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_scroll_menu
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_scroll_menu))))
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_scroll_menu))}')
         return new_scroll_menu
 
 
-    def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0, checked_char='X') -> py_cui.widgets.CheckBoxMenu:
+    def add_checkbox_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0, checked_char: str='X') -> 'py_cui.widgets.CheckBoxMenu':
         """Function that adds a new checkbox menu to the CUI grid
 
         Parameters
@@ -516,7 +525,7 @@ def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=
             A reference to the created checkbox object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_checkbox_menu = py_cui.widgets.CheckBoxMenu(id,
                                                         title,
                                                         self._grid,
@@ -528,15 +537,16 @@ def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=
                                                         pady,
                                                         self._logger,
                                                         checked_char)
-        new_checkbox_menu._assign_renderer(self._renderer)
-        self._widgets[id]  = new_checkbox_menu
+        if self._renderer is not None:
+            new_checkbox_menu._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_checkbox_menu
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_checkbox_menu))))
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_checkbox_menu))}')
         return new_checkbox_menu
 
 
-    def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '', password = False) -> py_cui.widgets.TextBox:
+    def add_text_box(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '', password: bool = False) -> 'py_cui.widgets.TextBox':
         """Function that adds a new text box to the CUI grid
 
         Parameters
@@ -566,7 +576,7 @@ def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx =
             A reference to the created textbox object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_text_box = py_cui.widgets.TextBox(id,
                                               title,
                                               self._grid,
@@ -577,15 +587,16 @@ def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx =
                                               self._logger,
                                               initial_text,
                                               password)
-        new_text_box._assign_renderer(self._renderer)
-        self._widgets[id]    = new_text_box
+        if self._renderer is not None:
+            new_text_box._assign_renderer(self._renderer)
+        self.get_widgets()[id]    = new_text_box
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_text_box))))
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_text_box))}')
         return new_text_box
 
 
-    def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '') -> py_cui.widgets.ScrollTextBlock:
+    def add_text_block(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '') -> 'py_cui.widgets.ScrollTextBlock':
         """Function that adds a new text block to the CUI grid
 
         Parameters
@@ -613,7 +624,7 @@ def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx
             A reference to the created textblock object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_text_block = py_cui.widgets.ScrollTextBlock(id,
                                                         title,
                                                         self._grid,
@@ -625,15 +636,16 @@ def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx
                                                         pady,
                                                         self._logger,
                                                         initial_text)
-        new_text_block._assign_renderer(self._renderer)
-        self._widgets[id]  = new_text_block
+        if self._renderer is not None:
+            new_text_block._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_text_block
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_text_block))))
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_text_block))}')
         return new_text_block
 
 
-    def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0) -> py_cui.widgets.Label:
+    def add_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.Label':
         """Function that adds a new label to the CUI grid
 
         Parameters
@@ -659,7 +671,7 @@ def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1,
             A reference to the created label object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_label = py_cui.widgets.Label(id,
                                          title,
                                          self._grid,
@@ -670,13 +682,14 @@ def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1,
                                          padx,
                                          pady,
                                          self._logger)
-        new_label._assign_renderer(self._renderer)
-        self._widgets[id]  = new_label
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_label))))
+        if self._renderer is not None:
+            new_label._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_label
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_label))}')
         return new_label
 
 
-    def add_block_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, center=True) -> py_cui.widgets.BlockLabel:
+    def add_block_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, center: bool=True) -> 'py_cui.widgets.BlockLabel':
         """Function that adds a new block label to the CUI grid
 
         Parameters
@@ -704,7 +717,7 @@ def add_block_label(self, title, row, column, row_span = 1, column_span = 1, pad
             A reference to the created block label object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_label = py_cui.widgets.BlockLabel(id,
                                               title,
                                               self._grid,
@@ -716,13 +729,14 @@ def add_block_label(self, title, row, column, row_span = 1, column_span = 1, pad
                                               pady,
                                               center,
                                               self._logger)
-        new_label._assign_renderer(self._renderer)
-        self._widgets[id]  = new_label
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_label))))
+        if self._renderer is not None:
+            new_label._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_label
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_label))}')
         return new_label
 
 
-    def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, command=None) -> py_cui.widgets.Button:
+    def add_button(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, command: Callable[[],Any]=None) -> 'py_cui.widgets.Button':
         """Function that adds a new button to the CUI grid
 
         Parameters
@@ -750,7 +764,7 @@ def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1
             A reference to the created button object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_button = py_cui.widgets.Button(id,
                                            title,
                                            self._grid,
@@ -762,17 +776,18 @@ def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1
                                            pady,
                                            self._logger,
                                            command)
-        new_button._assign_renderer(self._renderer)
-        self._widgets[id]  = new_button
+        if self._renderer is not None:
+            new_button._assign_renderer(self._renderer)
+        self.get_widgets()[id]  = new_button
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_button))))
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_button))}')
         return new_button
 
     
-    def add_slider(self, title, row, column, row_span=1,
-                   column_span=1, padx=1, pady=0,
-                   min_val=0, max_val=100, step=1, init_val=0) -> py_cui.controls.slider.SliderWidget:
+    def add_slider(self, title: str, row: int, column: int, row_span: int=1,
+                   column_span: int=1, padx: int=1, pady: int=0,
+                   min_val: int=0, max_val: int=100, step: int=1, init_val: int=0) -> 'py_cui.controls.slider.SliderWidget':
         """Function that adds a new label to the CUI grid
 
         Parameters
@@ -806,7 +821,7 @@ def add_slider(self, title, row, column, row_span=1,
             A reference to the created slider object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_slider = py_cui.controls.slider.SliderWidget(id,
                                                          title,
                                                          self._grid,
@@ -821,14 +836,38 @@ def add_slider(self, title, row, column, row_span=1,
                                                          max_val,
                                                          step,
                                                          init_val)
-        new_slider._assign_renderer(self._renderer)
-        self._widgets[id] = new_slider
-        self._logger.info('Adding widget {} w/ ID {} of type {}'
-                           .format(title, id, str(type(new_slider))))
+        if self._renderer is not None:
+            new_slider._assign_renderer(self._renderer)
+        self.get_widgets()[id] = new_slider
+        self._logger.info(f'Adding widget {title} w/ ID {id} of type {str(type(new_slider))}')
         return new_slider
 
 
-    def get_element_at_position(self, x, y):
+    def forget_widget(self, widget : 'py_cui.widgets.Widget') -> None:
+        """Function that is used to destroy or "forget" widgets. Forgotten widgets will no longer be drawn
+
+        Parameters
+        ----------
+        widget : py_cui.widgets.Widget
+            Widget to remove from the UI
+        
+        Raises
+        ------
+        TypeError
+            If input parameter is not of the py_cui widget type
+        KeyError
+            If input widget does not exist in the current UI or has already been removed.
+        """
+
+        if not isinstance(widget, py_cui.widgets.Widget):
+            raise TypeError('Argument widget must by of type py_cui.widgets.Widget!')
+        elif widget.get_id() not in self.get_widgets().keys():
+            raise KeyError(f'Widget with id {widget.get_id()} has already been removed from the UI!')
+        else:
+            self.get_widgets()[widget.get_id()] = None
+        
+
+    def get_element_at_position(self, x: int, y: int) -> Optional['py_cui.ui.UIElement']:
         """Returns containing widget for character position
 
         Parameters
@@ -846,14 +885,17 @@ def get_element_at_position(self, x, y):
 
         if self._popup is not None and self._popup._contains_position(x, y):
             return self._popup
+
         elif self._popup is None:
             for widget_id in self.get_widgets().keys():
-                if self.get_widgets()[widget_id]._contains_position(x, y):
-                    return self.get_widgets()[widget_id]
+                widget = self.get_widgets()[widget_id] 
+                if widget is not None:
+                    if widget._contains_position(x, y):
+                        return widget 
         return None
 
 
-    def _get_horizontal_neighbors(self, widget, direction):
+    def _get_horizontal_neighbors(self, widget: 'py_cui.widgets.Widget', direction: int) -> Optional[List[int]]:
         """Gets all horizontal (left, right) neighbor widgets
 
         Parameters
@@ -887,21 +929,21 @@ def _get_horizontal_neighbors(self, widget, direction):
         for col in range(col_range_start, col_range_stop):
             for row in range(row_start, row_start + row_span):
                 for widget_id in self.get_widgets().keys():
-                    if self.get_widgets()[widget_id]._is_row_col_inside(row, col) and widget_id not in id_list:
-                        id_list.append(widget_id)
+                    item_value = self.get_widgets()[widget_id] #using temporary variable, for mypy
+                    if item_value is not None:
+                        if item_value._is_row_col_inside(row, col) and widget_id not in id_list:
+                            id_list.append(widget_id)
 
         if direction == py_cui.keys.KEY_LEFT_ARROW:
             id_list.reverse()
 
-        self._logger.info('Neighbors with ids {} for cell {},{} span {},{}'.format(id_list,
-                                                                                   row_start,
-                                                                                   col_start,
-                                                                                   row_span,
-                                                                                   col_span))
+        self._logger.debug(f'Neighbors with ids {id_list} for cell \
+                             {row_start},{col_start} span {row_span},{col_span}')
+
         return id_list
 
 
-    def _get_vertical_neighbors(self, widget, direction):
+    def _get_vertical_neighbors(self, widget: 'py_cui.widgets.Widget', direction: int) -> Optional[List[int]]:
         """Gets all vertical (up, down) neighbor widgets
 
         Parameters
@@ -935,23 +977,23 @@ def _get_vertical_neighbors(self, widget, direction):
         for row in range(row_range_start, row_range_stop):
             for col in range(col_start, col_start + col_span):
                 for widget_id in self.get_widgets().keys():
-                    if self.get_widgets()[widget_id]._is_row_col_inside(row, col) and widget_id not in id_list:
-                        id_list.append(widget_id)
+                    item_value = self.get_widgets()[widget_id] 
+                    if item_value is not None:
+                        if item_value._is_row_col_inside(row, col) and widget_id not in id_list:
+                            id_list.append(widget_id)
 
         if direction == py_cui.keys.KEY_UP_ARROW:
             id_list.reverse()
 
-        self._logger.info('Neighbors with ids {} for cell {},{} span {},{}'.format(id_list,
-                                                                                   row_start,
-                                                                                   col_start,
-                                                                                   row_span,
-                                                                                   col_span))
+        self._logger.debug(f'Neighbors with ids {id_list} for cell \
+                             {row_start},{col_start} span {row_span},{col_span}')
+
         return id_list
 
     # CUI status functions. Used to switch between widgets, set the mode, and
     # identify neighbors for overview mode
 
-    def _check_if_neighbor_exists(self, direction):
+    def _check_if_neighbor_exists(self, direction: int) -> Optional[int]:
         """Function that checks if widget has neighbor in specified cell.
 
         Used for navigating CUI, as arrow keys find the immediate neighbor
@@ -963,27 +1005,29 @@ def _check_if_neighbor_exists(self, direction):
 
         Returns
         -------
-        widget_id : str
+        widget_id : int  
             The widget neighbor ID if found, None otherwise
         """
 
-        start_widget                = self.get_widgets()[self._selected_widget]
+        if self._selected_widget is not None:
+            start_widget: Optional[py_cui.widgets.Widget] = self.get_widgets()[self._selected_widget]
 
         # Find all the widgets in the given row or column
-        neighbors = []
-        if direction in [py_cui.keys.KEY_DOWN_ARROW, py_cui.keys.KEY_UP_ARROW]:
-            neighbors = self._get_vertical_neighbors(start_widget, direction)
-        elif direction in [py_cui.keys.KEY_RIGHT_ARROW, py_cui.keys.KEY_LEFT_ARROW]:
-            neighbors = self._get_horizontal_neighbors(start_widget, direction)
-
-        if len(neighbors) == 0:
+        neighbors: Optional[List[int]] = []
+        if start_widget is not None:
+            if direction in [py_cui.keys.KEY_DOWN_ARROW, py_cui.keys.KEY_UP_ARROW]:
+                neighbors = self._get_vertical_neighbors(start_widget, direction)
+            elif direction in [py_cui.keys.KEY_RIGHT_ARROW, py_cui.keys.KEY_LEFT_ARROW]:
+                neighbors = self._get_horizontal_neighbors(start_widget, direction)
+
+        if neighbors is None or len(neighbors) == 0:
             return None
 
         # We select the best match to jump to (first neighbor)
         return neighbors[0]
 
 
-    def get_selected_widget(self):
+    def get_selected_widget(self) -> Optional['py_cui.widgets.Widget']:
         """Function that gets currently selected widget
 
         Returns
@@ -999,23 +1043,23 @@ def get_selected_widget(self):
             return None
 
 
-    def set_selected_widget(self, widget_id):
+    def set_selected_widget(self, widget_id: int) -> None:
         """Function that sets the selected widget for the CUI
 
         Parameters
         ----------
-        widget_id : str
+        widget_id : int 
             the id of the widget to select
         """
 
         if widget_id in self.get_widgets().keys():
-            self._logger.info('Setting selected widget to ID {}'.format(widget_id))
+            self._logger.debug(f'Setting selected widget to ID {widget_id}')
             self._selected_widget = widget_id
         else:
-            self._logger.warn('Widget w/ ID {} does not exist among current widgets.'.format(widget_id))
+            self._logger.warn(f'Widget w/ ID {widget_id} does not exist among current widgets.')
 
 
-    def lose_focus(self):
+    def lose_focus(self) -> None:
         """Function that forces py_cui out of focus mode.
 
         After popup is called, focus is lost
@@ -1024,12 +1068,15 @@ def lose_focus(self):
         if self._in_focused_mode:
             self._in_focused_mode = False
             self.status_bar.set_text(self._init_status_bar_text)
-            self.get_widgets()[self._selected_widget].set_selected(False)
+            if self._selected_widget is not None:
+                widget = self.get_widgets()[self._selected_widget]
+                if widget is not None:
+                    widget.set_selected(False)
         else:
             self._logger.info('lose_focus: Not currently in focus mode')
 
 
-    def move_focus(self, widget, auto_press_buttons=True):
+    def move_focus(self, widget: 'py_cui.widgets.Widget', auto_press_buttons: bool=True) -> None:
         """Moves focus mode to different widget
 
         Parameters
@@ -1040,12 +1087,13 @@ def move_focus(self, widget, auto_press_buttons=True):
 
         self.lose_focus()
         self.set_selected_widget(widget.get_id())
+
         # If autofocus buttons is selected, we automatically process the button command and reset to overview mode
         if self._auto_focus_buttons and auto_press_buttons and isinstance(widget, py_cui.widgets.Button):
             if widget.command is not None:
                 widget.command()
 
-            self._logger.info('Moved focus to button {} - ran autofocus command'.format(widget.get_title()))
+            self._logger.debug(f'Moved focus to button {widget.get_title()} - ran autofocus command')
 
         elif self._auto_focus_buttons and isinstance(widget, py_cui.widgets.Button):
             self.status_bar.set_text(self._init_status_bar_text)
@@ -1053,10 +1101,11 @@ def move_focus(self, widget, auto_press_buttons=True):
             widget.set_selected(True)
             self._in_focused_mode = True
             self.status_bar.set_text(widget.get_help_text())
-        self._logger.info('Moved focus to widget {}'.format(widget.get_title()))
 
+        self._logger.debug(f'Moved focus to widget {widget.get_title()}')
 
-    def _cycle_widgets(self, reverse=False):
+
+    def _cycle_widgets(self, reverse: bool=False) -> None:
         """Function that is fired if cycle key is pressed to move to next widget
 
         Parameters
@@ -1066,29 +1115,37 @@ def _cycle_widgets(self, reverse=False):
         """
 
         num_widgets = len(self.get_widgets().keys())
-        current_widget_num = int(self._selected_widget.split('Widget')[1])
-
-        if not reverse:
-            next_widget_num = current_widget_num + 1
-            if next_widget_num == num_widgets:
-                next_widget_num = 0
-            cycle_key = self._forward_cycle_key
-        else:
-            next_widget_num = current_widget_num - 1
-            if next_widget_num < 0:
-                next_widget_num = num_widgets - 1
-            cycle_key = self._reverse_cycle_key
-
-        current_widget_id = 'Widget{}'.format(current_widget_num)
-        next_widget_id = 'Widget{}'.format(next_widget_num)
-        if self._in_focused_mode and cycle_key in self.get_widgets()[current_widget_id]._key_commands.keys():
-            # In the event that we are focusing on a widget with that key defined, we do not cycle.
-            pass
-        else:
-            self.move_focus(self.get_widgets()[next_widget_id], auto_press_buttons=False)
+        current_widget_num: Optional[int] = self._selected_widget
+
+        if current_widget_num is not None:
+            if not reverse:
+                next_widget_num = current_widget_num + 1
+                if self.get_widgets()[next_widget_num] is None:
+                    if next_widget_num == num_widgets:
+                        next_widget_num = 0
+                    next_widget_num = next_widget_num + 1
+                cycle_key = self._forward_cycle_key
+            else:
+                next_widget_num = current_widget_num - 1
+                if self.get_widgets()[next_widget_num] is None:
+                    if next_widget_num < 0:
+                        next_widget_num = num_widgets - 1
+                    next_widget_num = next_widget_num + 1
+                cycle_key = self._reverse_cycle_key
+
+            current_widget_id: int = current_widget_num
+            next_widget_id: int = next_widget_num
+        current_widget = self.get_widgets()[current_widget_id] 
+        next_widget = self.get_widgets()[next_widget_id] 
+        if current_widget and next_widget is not None: #pls check again
+            if self._in_focused_mode and cycle_key in current_widget._key_commands.keys():
+                # In the event that we are focusing on a widget with that key defined, we do not cycle.
+                pass
+            else:
+                self.move_focus(next_widget, auto_press_buttons=False)
 
 
-    def add_key_command(self, key, command):
+    def add_key_command(self, key: int, command: Callable[[],Any]) -> None:
         """Function that adds a keybinding to the CUI when in overview mode
 
         Parameters
@@ -1103,7 +1160,7 @@ def add_key_command(self, key, command):
 
     # Popup functions. Used to display messages, warnings, and errors to the user.
 
-    def show_message_popup(self, title, text):
+    def show_message_popup(self, title: str, text: str, color: int = WHITE_ON_BLACK) -> None:
         """Shows a message popup
 
         Parameters
@@ -1112,14 +1169,15 @@ def show_message_popup(self, title, text):
             Message title
         text : str
             Message text
+        color: int
+            Popup color with format FOREGOUND_ON_BACKGROUND. See colors module. Default: WHITE_ON_BLACK.
         """
 
-        color       = WHITE_ON_BLACK
         self._popup = py_cui.popups.MessagePopup(self, title, text, color, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_warning_popup(self, title, text):
+    def show_warning_popup(self, title: str, text: str) -> None:
         """Shows a warning popup
 
         Parameters
@@ -1130,12 +1188,10 @@ def show_warning_popup(self, title, text):
             Warning text
         """
 
-        color       = YELLOW_ON_BLACK
-        self._popup = py_cui.popups.MessagePopup(self, 'WARNING - ' + title, text, color, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self.show_message_popup(title=title, text=text, color=YELLOW_ON_BLACK)
 
 
-    def show_error_popup(self, title, text):
+    def show_error_popup(self, title: str, text: str) -> None:
         """Shows an error popup
 
         Parameters
@@ -1146,12 +1202,10 @@ def show_error_popup(self, title, text):
             Error text
         """
 
-        color       = RED_ON_BLACK
-        self._popup = py_cui.popups.MessagePopup(self, 'ERROR - ' + title, text, color, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self.show_message_popup(title=title, text=text, color=RED_ON_BLACK)
 
 
-    def show_yes_no_popup(self, title, command):
+    def show_yes_no_popup(self, title: str, command: Callable[[bool], Any]):
         """Shows a yes/no popup.
 
         The 'command' parameter must be a function with a single boolean parameter
@@ -1164,12 +1218,12 @@ def show_yes_no_popup(self, title, command):
             A function taking in a single boolean parameter. Will be fired with True if yes selected, false otherwise
         """
 
-        color       = WHITE_ON_BLACK
+        color = WHITE_ON_BLACK
         self._popup = py_cui.popups.YesNoPopup(self, title + '- (y/n)', 'Yes - (y), No - (n)', color, command, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_text_box_popup(self, title, command, password=False):
+    def show_text_box_popup(self, title: str, command: Callable[[str], Any], password: bool=False):
         """Shows a textbox popup.
 
         The 'command' parameter must be a function with a single string parameter
@@ -1184,12 +1238,12 @@ def show_text_box_popup(self, title, command, password=False):
             If true, write characters as '*'
         """
 
-        color       = WHITE_ON_BLACK
+        color = WHITE_ON_BLACK
         self._popup = py_cui.popups.TextBoxPopup(self, title, color, command, self._renderer, password, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_menu_popup(self, title, menu_items, command, run_command_if_none=False):
+    def show_menu_popup(self, title: str, menu_items: List[str], command: Callable[[str], Any], run_command_if_none: bool=False):
         """Shows a menu popup.
 
         The 'command' parameter must be a function with a single string parameter
@@ -1206,12 +1260,12 @@ def show_menu_popup(self, title, menu_items, command, run_command_if_none=False)
             If True, will run command passing in None if no menu item selected.
         """
 
-        color       = WHITE_ON_BLACK
+        color = WHITE_ON_BLACK
         self._popup = py_cui.popups.MenuPopup(self, menu_items, title, color, command, self._renderer, self._logger, run_command_if_none)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_loading_icon_popup(self, title, message, callback=None):
+    def show_loading_icon_popup(self, title: str, message: str, callback: Callable[[],Any]=None):
         """Shows a loading icon popup
 
         Parameters
@@ -1226,14 +1280,15 @@ def show_loading_icon_popup(self, title, message, callback=None):
 
         if callback is not None:
             self._post_loading_callback = callback
+            self._logger.debug(f'Post loading callback funciton set to {str(callback)}')
 
-        color         = WHITE_ON_BLACK
+        color = WHITE_ON_BLACK
         self._loading = True
-        self._popup   = py_cui.popups.LoadingIconPopup(self, title, message, color, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._popup = py_cui.popups.LoadingIconPopup(self, title, message, color, self._renderer, self._logger)
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_loading_bar_popup(self, title, num_items, callback=None):
+    def show_loading_bar_popup(self, title: str, num_items: List[int], callback: Callable[[],Any]=None) -> None:
         """Shows loading bar popup.
 
         Use 'increment_loading_bar' to show progress
@@ -1250,14 +1305,15 @@ def show_loading_bar_popup(self, title, num_items, callback=None):
 
         if callback is not None:
             self._post_loading_callback = callback
+            self._logger.debug(f'Post loading callback funciton set to {str(callback)}')
 
-        color         = WHITE_ON_BLACK
+        color = WHITE_ON_BLACK
         self._loading = True
-        self._popup   = py_cui.popups.LoadingBarPopup(self, title, num_items, color, self._renderer, self._logger)
-        self._logger.info('Opened {} popup with title {}'.format(str(type(self._popup)), self._popup.get_title()))
+        self._popup = py_cui.popups.LoadingBarPopup(self, title, num_items, color, self._renderer, self._logger)
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
 
-    def show_form_popup(self, title, fields, passwd_fields=[], required=[], callback=None):
+    def show_form_popup(self, title: str, fields: List[str], passwd_fields: List[str]=[], required: List[str]=[], callback: Callable[[],Any]=None) -> None:
         """Shows form popup.
 
         Used for inputting several fields worth of values
@@ -1276,34 +1332,56 @@ def show_form_popup(self, title, fields, passwd_fields=[], required=[], callback
             If not none, fired after loading is completed. Must be a no-arg function
         """
 
-        self._popup = py_cui.dialogs.form.FormPopup(self, fields, passwd_fields, required, {}, title, py_cui.WHITE_ON_BLACK, self._renderer, self._logger)
+        self._popup = py_cui.dialogs.form.FormPopup(self, 
+                                                    fields, 
+                                                    passwd_fields, 
+                                                    required, 
+                                                    {}, 
+                                                    title, 
+                                                    py_cui.WHITE_ON_BLACK, 
+                                                    self._renderer, 
+                                                    self._logger)
+
         if callback is not None:
             self._popup.set_on_submit_action(callback)
+            self._logger.debug(f'Form enter callback funciton set to {str(callback)}')
 
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with title {title}')
 
-    def show_filedialog_popup(self, popup_type='openfile', initial_dir='.', callback=None, ascii_icons=True, limit_extensions=[]):
+
+    def show_filedialog_popup(self, popup_type: str='openfile', initial_dir: str ='.', callback: Callable[[],Any]=None, ascii_icons: bool=True, limit_extensions: List[str]=[]) -> None:
         """Shows form popup.
 
         Used for inputting several fields worth of values
 
         Parameters
         ---------
-        title : str
-            Message title
-        fields : List[str]
-            Names of each individual field
-        passwd_fields : List[str]
-            Field names that should have characters hidden
-        required : List[str]
-            Fields that are required before submission
-        callback=None : Function
-            If not none, fired after loading is completed. Must be a no-arg function
+        popup_type : str
+            Type of filedialog popup - either openfile, opendir, or saveas
+        initial_dir : os.PathLike
+            Path to directory in which to open the file dialog, default "."
+        callback=None : Callable
+            If not none, fired after loading is completed. Must be a no-arg function, default=None
+        ascii_icons : bool
+            Compatibility option - use ascii icons instead of unicode file/folder icons, default True
+        limit_extensions : List[str]
+            Only show files with extensions in this list if not empty. Default, []
         """
 
-        self._popup = py_cui.dialogs.filedialog.FileDialogPopup(self, callback, initial_dir, popup_type, ascii_icons, limit_extensions, py_cui.WHITE_ON_BLACK, self._renderer, self._logger)
+        self._popup = py_cui.dialogs.filedialog.FileDialogPopup(self, 
+                                                                callback, 
+                                                                initial_dir, 
+                                                                popup_type, 
+                                                                ascii_icons, 
+                                                                limit_extensions, 
+                                                                py_cui.WHITE_ON_BLACK, 
+                                                                self._renderer, 
+                                                                self._logger)
 
+        self._logger.debug(f'Opened {str(type(self._popup))} popup with type {popup_type}')
 
-    def increment_loading_bar(self):
+
+    def increment_loading_bar(self) -> None:
         """Increments progress bar if loading bar popup is open
         """
 
@@ -1313,7 +1391,7 @@ def increment_loading_bar(self):
             self._logger.warn('No popup is currently opened.')
 
 
-    def stop_loading_popup(self):
+    def stop_loading_popup(self) -> None:
         """Leaves loading state, and closes popup.
 
         Must be called by user to escape loading.
@@ -1321,10 +1399,10 @@ def stop_loading_popup(self):
 
         self._loading = False
         self.close_popup()
-        self._logger.info('Stopping open loading popup')
+        self._logger.debug('Stopping open loading popup')
 
 
-    def close_popup(self):
+    def close_popup(self) -> None:
         """Closes the popup, and resets focus
         """
 
@@ -1332,27 +1410,38 @@ def close_popup(self):
         self._popup = None
 
 
-    def _refresh_height_width(self, height, width):
-        """Function that updates the height and width of the CUI based on terminal window size
+    def _refresh_height_width(self) -> None:
+        """Function that updates the height and width of the CUI based on terminal window size."""
+        
+        if self._simulated_terminal is None:
+            if self._stdscr is None:
+                term_size = shutil.get_terminal_size()
+                height  = term_size.lines
+                width   = term_size.columns
+            else:
+                # Use curses termsize when possible to fix resize bug on windows.
+                height, width = self._stdscr.getmaxyx()
+        else:
+            height  = self._simulated_terminal[0]
+            width   = self._simulated_terminal[1]
 
-        Parameters
-        ----------
-        height : int
-            Window height in terminal characters
-        width : int
-            Window width in terminal characters
-        """
+        height  = height - self.title_bar.get_height() - self.status_bar.get_height() - 2
+        
+        self._logger.debug(f'Resizing CUI to new dimensions {height} by {width}')
 
         self._height = height
         self._width  = width
         self._grid.update_grid_height_width(self._height, self._width)
-        for widget_id in self._widgets.keys():
-            self._widgets[widget_id].update_height_width()
+        for widget_id in self.get_widgets().keys():
+            widget = self.get_widgets()[widget_id] #using temp variable, for mypy
+            if widget is not None:
+                widget.update_height_width()
         if self._popup is not None:
             self._popup.update_height_width()
+        if self._logger._live_debug_element is not None:
+            self._logger._live_debug_element.update_height_width()
 
-
-    def get_absolute_size(self):
+    def get_absolute_size(self) -> Tuple[int,int]:
         """Returns dimensions of CUI
 
         Returns
@@ -1365,22 +1454,29 @@ def get_absolute_size(self):
 
     # Draw Functions. Function for drawing widgets, status bars, and popups
 
-    def _draw_widgets(self):
+    def _draw_widgets(self) -> None:
         """Function that draws all of the widgets to the screen
         """
 
-        for widget_key in self.get_widgets().keys():
-            if widget_key != self._selected_widget:
-                self.get_widgets()[widget_key]._draw()
+        for widget_id in self.get_widgets().keys():
+            if widget_id != self._selected_widget:
+                widget = self.get_widgets()[widget_id] 
+                if widget is not None:
+                    widget._draw()
 
         # We draw the selected widget last to support cursor location.
         if self._selected_widget is not None:
-            self.get_widgets()[self._selected_widget]._draw()
+            widget = self.get_widgets()[self._selected_widget]
+            if widget is not None:
+                widget._draw()
+
+        if self._logger is not None and self._logger.is_live_debug_enabled():
+            self._logger.draw_live_debug()
 
         self._logger.info('Drew widgets')
 
 
-    def _draw_status_bars(self, stdscr, height, width):
+    def _draw_status_bars(self, stdscr, height: int, width: int) -> None:
         """Draws status bar and title bar
 
         Parameters
@@ -1393,18 +1489,18 @@ def _draw_status_bars(self, stdscr, height, width):
             Window width in terminal characters
         """
 
-        if self.status_bar is not None:
+        if self.status_bar is not None and self.status_bar.get_height() > 0:
             stdscr.attron(curses.color_pair(self.status_bar.get_color()))
             stdscr.addstr(height + 3, 0, fit_text(width, self.status_bar.get_text()))
             stdscr.attroff(curses.color_pair(self.status_bar.get_color()))
 
-        if self.title_bar is not None:
+        if self.title_bar is not None and self.title_bar.get_height() > 0:
             stdscr.attron(curses.color_pair(self.title_bar.get_color()))
             stdscr.addstr(0, 0, fit_text(width, self._title, center=True))
             stdscr.attroff(curses.color_pair(self.title_bar.get_color()))
 
 
-    def _display_window_warning(self, stdscr, error_info):
+    def _display_window_warning(self, stdscr, error_info: str) -> None:
         """Function that prints some basic error info if there is an error with the CUI
 
         Parameters
@@ -1418,14 +1514,14 @@ def _display_window_warning(self, stdscr, error_info):
         stdscr.clear()
         stdscr.attron(curses.color_pair(RED_ON_BLACK))
         stdscr.addstr(0, 0, 'Error displaying CUI!!!')
-        stdscr.addstr(1, 0, 'Error Type: {}'.format(error_info))
+        stdscr.addstr(1, 0, f'Error Type: {error_info}')
         stdscr.addstr(2, 0, 'Most likely terminal dimensions are too small.')
         stdscr.attroff(curses.color_pair(RED_ON_BLACK))
         stdscr.refresh()
-        self._logger.info('Encountered error -> {}'.format(error_info))
+        self._logger.error(f'Encountered error -> {error_info}')
 
 
-    def _handle_key_presses(self, key_pressed):
+    def _handle_key_presses(self, key_pressed: int) -> None:
         """Function that handles all main loop key presses.
 
         Parameters
@@ -1439,18 +1535,29 @@ def _handle_key_presses(self, key_pressed):
             return
 
         selected_widget = self.get_widgets()[self._selected_widget]
+        if selected_widget is None:
+            return
+        # If logging is enabled, the Ctrl + D key code will enable "live-debug"
+        # mode, where debug messages are printed on the screen
+        if self._logger is not None and self._toggle_live_debug_key is not None:
+            if key_pressed == self._toggle_live_debug_key:
+                self._logger.toggle_live_debug()
+
+        # If we are in live debug mode, we only handle keypresses for the live debug UI element
+        if self._logger is not None and self._logger.is_live_debug_enabled():
+            self._logger._live_debug_element._handle_key_press(key_pressed)
 
         # If we are in focus mode, the widget has all of the control of the keyboard except
         # for the escape key, which exits focus mode.
-        if self._in_focused_mode and self._popup is None:
+        elif self._in_focused_mode and self._popup is None:
             if key_pressed == py_cui.keys.KEY_ESCAPE:
                 self.status_bar.set_text(self._init_status_bar_text)
                 self._in_focused_mode = False
                 selected_widget.set_selected(False)
-                self._logger.info('Exiting focus mode on widget {}'.format(selected_widget.get_title()))
+                self._logger.debug(f'Exiting focus mode on widget {selected_widget.get_title()}')
             else:
                 # widget handles remaining py_cui.keys
-                self._logger.info('Widget {} handling {} key'.format(selected_widget.get_title(), key_pressed))
+                self._logger.debug(f'Widget {selected_widget.get_title()} handling {key_pressed} key')
                 selected_widget._handle_key_press(key_pressed)
 
         # Otherwise, barring a popup, we are in overview mode, meaning that arrow py_cui.keys move between widgets, and Enter key starts focus mode
@@ -1461,7 +1568,7 @@ def _handle_key_presses(self, key_pressed):
             for key in self._keybindings.keys():
                 if key_pressed == key:
                     command = self._keybindings[key]
-                    self._logger.info('Detected binding for key {}, running command {}'.format(key_pressed, command.__name__))
+                    self._logger.info(f'Detected binding for key {key_pressed}, running command {command.__name__}')
                     command()
 
             # If not in focus mode, use the arrow py_cui.keys to move around the selectable widgets.
@@ -1470,19 +1577,21 @@ def _handle_key_presses(self, key_pressed):
                 neighbor = self._check_if_neighbor_exists(key_pressed)
             if neighbor is not None:
                 self.set_selected_widget(neighbor)
-                self._logger.info('Navigated to neighbor widget {}'.format(self.get_widgets()[self._selected_widget].get_title()))
+                widget = self.get_widgets()[self._selected_widget]
+                if widget is not None:
+                    self._logger.debug(f'Navigated to neighbor widget {widget.get_title()}')
 
         # if we have a popup, that takes key control from both overview and focus mode
         elif self._popup is not None:
-            self._logger.info('Popup {} handling key {}'.format(self._popup.get_title(), key_pressed))
+            self._logger.debug(f'Popup {self._popup.get_title()} handling key {key_pressed}')
             self._popup._handle_key_press(key_pressed)
 
 
-    def _draw(self, stdscr):
+    def _draw(self, stdscr) -> None: 
         """Main CUI draw loop called by start()
 
         Parameters
-        ----------
+        --------
         stdscr : curses Standard screen
             The screen buffer used for drawing CUI elements
         """
@@ -1506,7 +1615,7 @@ def _draw(self, stdscr):
             self._stdscr.timeout(self._refresh_timeout)
 
         # If user sets non-default border characters, update them here
-        if self._border_characters is not None:
+        if self._border_characters is not None and self._renderer is not None:
             self._renderer._set_border_renderer_chars(self._border_characters)
 
         # Loop where key_pressed is the last character pressed. Wait for exit key while no popup or focus mode
@@ -1520,15 +1629,6 @@ def _draw(self, stdscr):
                 # Initialization and size adjustment
                 stdscr.erase()
 
-                # find height width, adjust if status/title bar added. We decrement the height by 4 to account for status/title bar and padding
-                if self._simulated_terminal is None:
-                    height, width   = stdscr.getmaxyx()
-                else:
-                    height = self._simulated_terminal[0]
-                    width  = self._simulated_terminal[1]
-
-                height = height - 4
-
                 # If the user defined an update function to fire on each draw call,
                 # Run it here. This can of course be also handled user-side
                 # through a separate thread.
@@ -1538,9 +1638,8 @@ def _draw(self, stdscr):
                 # This is what allows the CUI to be responsive. Adjust grid size based on current terminal size
                 # Resize the grid and the widgets if there was a resize operation
                 if key_pressed == curses.KEY_RESIZE:
-                    self._logger.info('Resizing CUI to new dimensions {} by {}'.format(height, width))
                     try:
-                        self._refresh_height_width(height, width)
+                        self._refresh_height_width()
                     except py_cui.errors.PyCUIOutOfBoundsError as e:
                         self._logger.info('Resized terminal too small')
                         self._display_window_warning(stdscr, str(e))
@@ -1548,20 +1647,30 @@ def _draw(self, stdscr):
                 # Here we handle mouse click events globally, or pass them to the UI element to handle
                 elif key_pressed == curses.KEY_MOUSE:
                     self._logger.info('Detected mouse click')
-                    _, x, y, _, _ = curses.getmouse()
-                    in_element = self.get_element_at_position(x, y)
+                    
+                    valid_mouse_event = True
+                    try:
+                        id, x, y, _, mouse_event = curses.getmouse()
+                    except curses.error as e:
+                        valid_mouse_event = False
+                        self._logger.error(f'Failed to handle mouse event: {str(e)}')
+
+                    if valid_mouse_event:
+                        in_element = self.get_element_at_position(x, y)
+
+                        # In first case, we click inside already selected widget, pass click for processing
+                        if in_element is not None:
+                            self._logger.info(f'handling mouse press for elem: {in_element.get_title()}')
+                            in_element._handle_mouse_press(x, y, mouse_event)
 
-                    # In first case, we click inside already selected widget, pass click for processing
-                    if in_element is not None and in_element.is_selected():
-                        in_element._handle_mouse_press(x, y)
-                    # Otherwise, if not a popup, select the clicked on widget
-                    elif in_element is not None and not isinstance(in_element, py_cui.popups.Popup):
-                        self.move_focus(in_element)
-                        in_element._handle_mouse_press(x, y)
+                        # Otherwise, if not a popup, select the clicked on widget
+                        elif in_element is not None and not isinstance(in_element, py_cui.popups.Popup):
+                            self.move_focus(in_element)
+                            in_element._handle_mouse_press(x, y, mouse_event)
 
                 # If we have a post_loading_callback, fire it here
                 if self._post_loading_callback is not None and not self._loading:
-                    self._logger.info('Firing post-loading callback function {}'.format(self._post_loading_callback.__name__))
+                    self._logger.debug(f'Firing post-loading callback function {self._post_loading_callback.__name__}')
                     self._post_loading_callback()
                     self._post_loading_callback = None
 
@@ -1576,11 +1685,16 @@ def _draw(self, stdscr):
 
                 try:
                     # Draw status/title bar, and all widgets. Selected widget will be bolded.
-                    self._draw_status_bars(stdscr, height, width)
+                    self._draw_status_bars(stdscr, self._height, self._width)
                     self._draw_widgets()
                     # draw the popup if required
                     if self._popup is not None:
                         self._popup._draw()
+
+                    # If we are in live debug mode, we draw our debug messages
+                    if self._logger.is_live_debug_enabled():
+                        self._logger.draw_live_debug()
+
                 except curses.error as e:
                     self._logger.error('Curses error while drawing TUI')
                     self._display_window_warning(stdscr, str(e))
@@ -1612,7 +1726,7 @@ def _draw(self, stdscr):
         stdscr.refresh()
         curses.endwin()
         if self._on_stop is not None:
-            self._logger.info('Firing onstop function {}'.format(self._on_stop.__name__))
+            self._logger.debug(f'Firing onstop function {self._on_stop.__name__}')
             self._on_stop()
 
 
@@ -1626,6 +1740,7 @@ def __format__(self, fmt):
         """
 
         out = ''
-        for widget in self.get_widgets().keys():
-            out += '{}\n'.format(self.get_widgets()[widget].get_title())
+        for widget_id in self.get_widgets().keys():
+            if self.get_widgets()[widget_id] is not None:
+                out += f'{self.get_widgets()[widget_id].get_title()}\n'
         return out
diff --git a/py_cui/colors.py b/py_cui/colors.py
index 0cf55e2..667515c 100644
--- a/py_cui/colors.py
+++ b/py_cui/colors.py
@@ -8,6 +8,7 @@
 import py_cui
 import curses
 import re
+from typing import List, Tuple, Union
 
 
 # Curses color configuration - curses colors automatically work as pairs, so it was easiest to
@@ -167,7 +168,7 @@ class ColorRule:
         Flag to determine whether to strip whitespace before matching.
     """
 
-    def __init__(self, regex, color, selected_color, rule_type, match_type, region, include_whitespace, logger):
+    def __init__(self, regex: str, color: int, selected_color: int, rule_type: str, match_type: str, region: List[int], include_whitespace: bool, logger):
         """Constructor for ColorRule object
             
         Parameters
@@ -205,7 +206,7 @@ def __init__(self, regex, color, selected_color, rule_type, match_type, region,
         self._logger               = logger
 
 
-    def _check_match(self, line):
+    def _check_match(self, line: str) -> bool:
         """Checks if the color rule matches a line
         
         Parameters
@@ -242,7 +243,7 @@ def _check_match(self, line):
         return False
 
 
-    def _generate_fragments_regex(self, widget, render_text, selected):
+    def _generate_fragments_regex(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], render_text:str, selected) -> List[List[Union[int,str]]]:
         """Splits text into fragments based on regular expression
         
         Parameters
@@ -258,7 +259,7 @@ def _generate_fragments_regex(self, widget, render_text, selected):
             the render text split into fragments of strings paired with colors
         """
 
-        fragments = []
+        fragments: List[List[Union[int,str]]] = []
         matches = re.findall(self._regex, render_text)
         current_render_text = render_text
         for match in matches:
@@ -280,9 +281,9 @@ def _generate_fragments_regex(self, widget, render_text, selected):
         return fragments
 
 
-    def _split_text_on_region(self, widget, render_text, selected):
+    def _split_text_on_region(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], render_text: str, selected) -> List[List[Union[str,int]]]:   # renderer._generate_text_color_fragments passes a uielement and not a widget
         """Splits text into fragments based on region
-        
+
         Parameters
         ----------
         widget : py_cui.Widget
@@ -296,7 +297,7 @@ def _split_text_on_region(self, widget, render_text, selected):
             the render text split into fragments of strings paired with colors
         """
 
-        fragments = []
+        fragments: List[List[Union[int,str]]] = []
         
         if self._region is None or len(render_text) < self._region[0]:
             if selected:
@@ -322,7 +323,7 @@ def _split_text_on_region(self, widget, render_text, selected):
         return fragments
 
 
-    def generate_fragments(self, widget, line, render_text, selected=False):
+    def generate_fragments(self, widget: Union['py_cui.widgets.Widget','py_cui.ui.UIElement'], line: str, render_text: str, selected=False) -> Tuple[List[List[Union[str,int]]],bool]:
         """Splits text into fragments if matched line to regex
         
         Parameters
@@ -341,10 +342,10 @@ def generate_fragments(self, widget, line, render_text, selected=False):
         matched : bool
             Boolean output saying if a match was found in the line.
         """
-
+        fragments: List[List[Union[int,str]]] = []
         match       = self._check_match(line)
         if selected:
-            fragments = [[render_text, widget.get_selected_color()]]
+            fragments = [[render_text, widget.get_selected_color()]] 
         else:
             fragments = [[render_text, widget.get_color()]]
         
@@ -360,6 +361,6 @@ def generate_fragments(self, widget, line, render_text, selected=False):
             elif self._match_type == 'region':
                 fragments = self._split_text_on_region(widget, render_text, selected)
         
-            self._logger.info('Generated fragments: {}'.format(fragments))
+            self._logger.debug(f'Generated fragments: {fragments}')
         
         return fragments, match
\ No newline at end of file
diff --git a/py_cui/controls/slider.py b/py_cui/controls/slider.py
index 3e6e6dc..b4b742c 100644
--- a/py_cui/controls/slider.py
+++ b/py_cui/controls/slider.py
@@ -1,11 +1,12 @@
 import py_cui.ui
 import py_cui.widgets
 import py_cui.popups
+import py_cui.errors
 
 
 class SliderImplementation(py_cui.ui.UIImplementation):
 
-    def __init__(self, min_val, max_val, init_val, step, logger):
+    def __init__(self, min_val: int, max_val: int, init_val: int, step: int, logger):
         super().__init__(logger)
 
         self._min_val = min_val
@@ -17,11 +18,10 @@ def __init__(self, min_val, max_val, init_val, step, logger):
 
         if self._cur_val < self._min_val or self._cur_val > self._max_val:
             raise py_cui.errors.PyCUIInvalidValue(
-                'initial value must be between {} and {}'
-                .format(self._min_val, self._max_val))
+                f'initial value must be between {self._min_val} and {self._max_val}')
 
 
-    def set_bar_char(self, char):
+    def set_bar_char(self, char: str) -> None:
         """
         Updates the character used to represent the slider bar.
 
@@ -31,7 +31,7 @@ def set_bar_char(self, char):
             Character to represent progressive bar.
         """
 
-        assert len(char) == 1, "char should contain exactly one character, got {} instead.".format(len(char))
+        assert len(char) == 1, f"char should contain exactly one character, got {len(char)} instead."
         self._bar_char = char
 
 
@@ -62,7 +62,7 @@ def update_slider_value(self, offset: int) -> float:
         return self._cur_val
 
 
-    def get_slider_value(self):
+    def get_slider_value(self) -> float:
         """
         Returns current slider value.
 
@@ -75,7 +75,7 @@ def get_slider_value(self):
         return self._cur_val
 
 
-    def set_slider_step(self, step):
+    def set_slider_step(self, step: int) -> None:
         """
         Changes the step value.
 
@@ -120,46 +120,46 @@ def __init__(self, id, title, grid, row, column, row_span, column_span,
         self.set_help_text("Focus mode on Slider. Use left/right to adjust value. Esc to exit.")
 
 
-    def toggle_title(self):
+    def toggle_title(self) -> None:
         """Toggles visibility of the widget's name.
         """
 
         self._title_enabled = not self._title_enabled
 
 
-    def toggle_border(self):
+    def toggle_border(self) -> None:
         """Toggles visibility of the widget's border.
         """
 
         self._border_enabled = not self._border_enabled
 
 
-    def toggle_value(self):
+    def toggle_value(self) -> None:
         """Toggles visibility of the widget's current value in integer.
         """
 
         self._display_value = not self._display_value
 
 
-    def align_to_top(self):
+    def align_to_top(self) -> None:
         """Aligns widget height to top.
         """
         self._alignment = "top"
 
 
-    def align_to_middle(self):
+    def align_to_middle(self) -> None:
         """Aligns widget height to middle. default configuration.
         """
         self._alignment = "mid"
 
 
-    def align_to_bottom(self):
+    def align_to_bottom(self) -> None:
         """Aligns widget height to bottom.
         """
         self._alignment = "btm"
 
 
-    def _custom_draw_with_border(self, start_y: int, content: str):
+    def _custom_draw_with_border(self, start_y: int, content: str) -> None:
         """
         Custom method made from renderer.draw_border to support alignment for bordered variants.
 
@@ -222,7 +222,7 @@ def _generate_bar(self, width: int) -> str:
         return progress
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class draw function.
         """
 
@@ -257,7 +257,7 @@ def _draw(self):
         self._renderer.unset_color_mode(self._color)
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """
         LEFT_ARROW decreases value, RIGHT_ARROW increases.
 
diff --git a/py_cui/debug.py b/py_cui/debug.py
index 2b04f8b..08d1248 100644
--- a/py_cui/debug.py
+++ b/py_cui/debug.py
@@ -8,9 +8,11 @@
 import logging
 import inspect
 import py_cui
+import datetime
+from typing import Any, Optional, Tuple
 
 
-def _enable_logging(logger, replace_log_file=True, filename='py_cui_log.txt', logging_level=logging.DEBUG):
+def _enable_logging(logger: 'PyCUILogger', replace_log_file: bool=True, filename: str='py_cui.log', logging_level=logging.DEBUG) :
     """Function that creates basic logging configuration for selected logger
 
     Parameters
@@ -30,24 +32,26 @@ def _enable_logging(logger, replace_log_file=True, filename='py_cui_log.txt', lo
         Only the custom PyCUILogger can be used here.
     """
 
+    # Remove existing log file if necessary
     abs_path = os.path.abspath(filename)
     if replace_log_file and os.path.exists(abs_path):
         os.remove(abs_path)
 
+    # Permission check and check if we are using custom py_cui logger
     if not os.access(os.path.dirname(abs_path), os.W_OK):
         raise PermissionError('You do not have permission to create py_cui.log file.')
 
     if not isinstance(logger, PyCUILogger):
         raise TypeError('Only the PyCUILogger can be used for logging in the py_cui module.')
 
+    # Create our logging utility objects
     log_file    = logging.FileHandler(filename)
     formatter   = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s | %(message)s')
     log_file.setFormatter(formatter)
     logger.addHandler(log_file)
     logger.setLevel(logging_level)
 
-
-def _initialize_logger(py_cui_root, name=None, custom_logger=True):
+def _initialize_logger(py_cui_root: 'py_cui.PyCUI', name: Optional[str]=None, custom_logger: bool=True) :
     """Function that retrieves an instance of either the default or custom py_cui logger.
     
     Parameters
@@ -77,6 +81,166 @@ def _initialize_logger(py_cui_root, name=None, custom_logger=True):
             logging._releaseLock()
 
 
+class LiveDebugImplementation(py_cui.ui.MenuImplementation):
+    """Implementation class for the live debug menu - builds off of the scroll menu implementation
+
+    Attributes
+    ----------
+    level : int
+        Debug level at which to display messages. Can be separate from the default logging level
+    _buffer_size : List[str]
+        Number of log messages to keep buffered in the live debug window
+    """
+
+
+    def __init__(self, parent_logger):
+        """Initializer for LiveDebugImplementation
+        """
+
+        super().__init__(parent_logger)
+        self.level      = logging.ERROR
+        # Make default buffer size 100
+        self._buffer_size           = 100
+
+
+    def print_to_buffer(self, msg: str, log_level) -> None:
+        """Override of default MenuImplementation add_item function
+
+        If items override the buffer pop the oldest log message
+
+        Parameters
+        ----------
+        msg : str
+            Log message to add
+        """
+
+        if len(self._view_items) == self._buffer_size:
+            self._view_items.pop(0)
+        self._view_items.append(f'{datetime.datetime.now()} - {log_level} | {msg}')
+
+
+class LiveDebugElement(py_cui.ui.UIElement, LiveDebugImplementation):
+    """UIElement class for the live debug utility. extends from base UIElement class and LiveDebugImplementation
+    """
+
+    def __init__(self, parent_logger):
+        """Initializer for LiveDebugElement class
+        """
+
+        LiveDebugImplementation.__init__(self, parent_logger)
+        py_cui.ui.UIElement.__init__(self, 'LiveDebug', 'PyCUI Live Debug', None, parent_logger)
+
+        # Initialize these to some dummy values to start with - will get overriden once 
+        # parent logger is assigned a root py_cui window
+        self._start_x = 5
+        self._start_y = 5
+        self._stop_x = 150
+        self._stop_y = 25
+
+
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
+        """Override of base UI element class function. Sets start position relative to entire UI size
+
+        Returns
+        -------
+        start_x, start_y : int, int
+            Start position x, y coords in terminal characters
+        """
+
+        start_x = int(self._logger.py_cui_root._width / 7) + 2
+        start_y = int(self._logger.py_cui_root._height / 7) + 2
+        return start_x, start_y 
+
+
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
+        """Override of base UI element class function. Sets stop position relative to entire UI size
+
+        Returns
+        -------
+        stop_x, stop_y : int, int
+            Stop position x, y coords in terminal characters
+        """
+
+        stop_x = 6 * int(self._logger.py_cui_root._width / 7) - 2
+        stop_y = 6 * int(self._logger.py_cui_root._height / 7) - 2
+        return stop_x, stop_y 
+
+
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
+        """Override of base class function, handles mouse press in menu
+
+        Parameters
+        ----------
+        x, y : int
+            Coordinates of mouse press
+        mouse_event : int
+            Key code for py_cui mouse event
+        """
+
+        py_cui.ui.UIElement._handle_mouse_press(x, y, mouse_event)
+        if mouse_event == py_cui.keys.LEFT_MOUSE_CLICK:
+            viewport_top = self._start_y + self._pady + 1
+            if viewport_top <= y and viewport_top + len(self._view_items) - self._top_view >= y:
+                elem_clicked = y - viewport_top + self._top_view
+                self.set_selected_item_index(elem_clicked)
+
+
+    def _handle_key_press(self, key_pressed: int) -> None:
+        """Override of base class function.
+
+        Essentially the same as the ScrollMenu widget _handle_key_press, with the exception that Esc breaks
+        out of live debug mode.
+
+        Parameters
+        ----------
+        key_pressed : int
+            The keycode of the pressed key
+        """
+
+        # If we have escape pressed, we break out of live debug mode
+        if key_pressed == py_cui.keys.KEY_ESCAPE:
+            self._logger.toggle_live_debug()
+
+        viewport_height = self.get_viewport_height()
+        if key_pressed == py_cui.keys.KEY_UP_ARROW:
+            self._scroll_up()
+        if key_pressed == py_cui.keys.KEY_DOWN_ARROW:
+            self._scroll_down(viewport_height)
+        if key_pressed == py_cui.keys.KEY_HOME:
+            self._jump_to_top()
+        if key_pressed == py_cui.keys.KEY_END:
+            self._jump_to_bottom(viewport_height)
+        if key_pressed == py_cui.keys.KEY_PAGE_UP:
+            self._jump_up()
+        if key_pressed == py_cui.keys.KEY_PAGE_DOWN:
+            self._jump_down(viewport_height)
+        
+
+    def _draw(self) -> None:
+        """Overrides base class draw function. Mostly a copy of ScrollMenu widget - but reverse item list
+        """
+
+        self._renderer.set_color_mode(py_cui.WHITE_ON_BLACK)
+        self._renderer.draw_border(self)
+        counter = self._pady + 1
+        line_counter = 0
+        for item in reversed(self._view_items):
+            line = str(item)
+            if line_counter < self._top_view:
+                line_counter = line_counter + 1
+            else:
+                if counter >= self._height - self._pady - 1:
+                    break
+                if line_counter == self._selected_item:
+                    self._renderer.draw_text(self, line, self._start_y + counter, selected=True)
+                else:
+                    self._renderer.draw_text(self, line, self._start_y + counter)
+                counter = counter + 1
+                line_counter = line_counter + 1
+        self._renderer.unset_color_mode(py_cui.WHITE_ON_BLACK)
+        self._renderer.reset_cursor(self)
+
+
 class PyCUILogger(logging.Logger):
     """Custom logger class for py_cui, extends the base logging.Logger Class
     
@@ -98,30 +262,62 @@ def __init__(self, name):
         """
 
         super(PyCUILogger, self).__init__(name)
-        self._live_debug_level      = logging.ERROR
         self._live_debug_enabled    = False
+        self.py_cui_root            = None
+        self._live_debug_element    = LiveDebugElement(self)
 
+ 
+    def is_live_debug_enabled(self):
+        return self._live_debug_enabled
 
-    def _assign_root_window(self, py_cui_root):
-        """Attaches logger to the root window for live debugging
+    def toggle_live_debug(self):
+        self._live_debug_enabled = not self._live_debug_enabled
+
+    def draw_live_debug(self):
+        """Function that draws the live debug UI element if applicable
+        """
+
+        if self.is_live_debug_enabled() and self.py_cui_root is not None:
+            self._live_debug_element._draw()
+
+
+    def _assign_root_window(self, py_cui_root: 'py_cui.PyCUI') -> None:
+        """Function that assigns a PyCUI root object to the logger. Important for live-debug hooks
+
+        Parameters
+        ----------
+        py_cui_root : PyCUI
+            Root PyCUI object for the application
         """
 
         if not isinstance(py_cui_root, py_cui.PyCUI):
             raise TypeError('py_cui_root type must be py_cui.PyCUI')
 
         self.py_cui_root = py_cui_root
+        self._live_debug_element.update_height_width()
 
 
-    def _get_debug_text(self, text):
+    def _get_debug_text(self, text: str) -> str:
         """Function that generates full debug text for the log
-        """
 
-        func = inspect.currentframe().f_back.f_back.f_code
-        return "{}: Function {} in {}:{}".format(text, func.co_name, os.path.basename(func.co_filename), func.co_firstlineno)
+        Parameters
+        ----------
+        text : str
+            Log message
+
+        Returns
+        -------
+        msg : str
+            Log message with function, file, and line num info
+        """
+        current_frame  = inspect.currentframe()
+        if current_frame and current_frame.f_back and current_frame.f_back.f_back  is not None:
+            func = current_frame.f_back.f_back.f_code
+        return f'{text}: Function {func.co_name} in {os.path.basename(func.co_filename)}:{func.co_firstlineno}'
     
     
-    def info(self, text):
-        """Adds stacktrace info to log
+    def info(self, msg: Any, *args, **kwargs) -> None : # to overcome signature mismatch in error
+        """Override of base logger info function to add hooks for live debug mode
         
         Parameters
         ----------
@@ -129,12 +325,14 @@ def info(self, text):
             The log text ot display
         """
 
-        debug_text = self._get_debug_text(text)
+        debug_text = self._get_debug_text(msg)
+        if self.level <= logging.INFO:
+            self._live_debug_element.print_to_buffer(debug_text, 'INFO')
         super().info(debug_text)
 
 
-    def debug(self, text):
-        """Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
+    def debug(self, msg: str, *args, **kwargs) -> None:
+        """Override of base logger debug function to add hooks for live debug mode
         
         Parameters
         ----------
@@ -142,17 +340,14 @@ def debug(self, text):
             The log text ot display
         """
 
-        debug_text = self._get_debug_text(text)
-        if self._live_debug_level == logging.DEBUG and self._live_debug_enabled:
-            if self.py_cui_root is not None:
-                self.py_cui_root.status_bar.set_text(debug_text)
-                super().debug(debug_text)
-        else:
-            super().debug(debug_text)
+        debug_text = self._get_debug_text(msg)
+        if self.level <= logging.DEBUG:
+            self._live_debug_element.print_to_buffer(debug_text, 'DEBUG')
+        super().debug(debug_text)
 
 
-    def warn(self, text):
-        """Function that allows for live debugging of py_cui programs by displaying log messages in the satus bar
+    def warn(self, msg: str, *args, **kwargs) -> None:
+        """Override of base logger warn function to add hooks for live debug mode
         
         Parameters
         ----------
@@ -160,17 +355,14 @@ def warn(self, text):
             The log text ot display
         """
 
-        debug_text = self._get_debug_text(text)
-        if self._live_debug_level < logging.WARN and self._live_debug_enabled:
-            if self.py_cui_root is not None:
-                self.py_cui_root.status_bar.set_text(debug_text)
-                super().debug(debug_text)
-        else:
-            super().warn(debug_text)
+        debug_text = self._get_debug_text(msg)
+        if self.level <= logging.WARN:
+            self._live_debug_element.print_to_buffer(debug_text, 'WARN')
+        super().warn(debug_text)
 
 
-    def error(self, text):
-        """Function that displays error messages live in status bar for py_cui logging
+    def error(self, msg: str, *args, **kwargs) -> None:
+        """Override of base logger error function to add hooks for live debug mode
         
         Parameters
         ----------
@@ -178,18 +370,23 @@ def error(self, text):
             The log text ot display
         """
 
-        debug_text = self._get_debug_text(text)
-        if self._live_debug_level < logging.ERROR and self._live_debug_enabled:
-            if self.py_cui_root is not None:
-                self.py_cui_root.status_bar.set_text(debug_text)
-                super().debug(debug_text)
-        else:
-            super().error(debug_text)
+        debug_text = self._get_debug_text(msg)
+        if self.level <= logging.ERROR:
+            self._live_debug_element.print_to_buffer(debug_text, 'ERROR')
+        super().error(debug_text)
 
 
-    def toggle_live_debug(self, level=logging.ERROR):
-        """Toggles live debugging mode
+    def critical(self, msg: str, *args, **kwargs) -> None:
+        """Override of base logger critical function to add hooks for live debug mode
+        
+        Parameters
+        ----------
+        text : str
+            The log text ot display
         """
 
-        self._live_debug_enabled    = not self._live_debug_enabled
-        self._live_debug_level      = level
\ No newline at end of file
+        debug_text = self._get_debug_text(msg)
+        if self.level <= logging.CRITICAL:
+            self._live_debug_element.print_to_buffer(debug_text, 'CRITICAL')
+        super().critical(debug_text)
+
diff --git a/py_cui/dialogs/filedialog.py b/py_cui/dialogs/filedialog.py
index 6f8946a..3a9d4c2 100644
--- a/py_cui/dialogs/filedialog.py
+++ b/py_cui/dialogs/filedialog.py
@@ -6,13 +6,14 @@
 import py_cui.popups
 import py_cui.colors
 import os
+from typing import List, Optional, Tuple
 
 # Imports used to detect hidden files
 import sys
 import stat
 
 
-def is_filepath_hidden(path):
+def is_filepath_hidden(path:  str) -> bool:
     """Function checks if file or folder is considered "hidden"
 
     Parameters
@@ -52,7 +53,7 @@ class FileDirElem:
         icon for file
     """
 
-    def __init__(self, elem_type, name, fullpath, ascii_icons=False):
+    def __init__(self, elem_type: str, name: str, fullpath: str, ascii_icons: bool=False):
         """Intializer for FilDirElem
         """
 
@@ -71,7 +72,7 @@ def __init__(self, elem_type, name, fullpath, ascii_icons=False):
             self._file_icon = '     '
 
 
-    def get_path(self):
+    def get_path(self) -> str:
         """Getter for path
 
         Returns
@@ -83,7 +84,7 @@ def get_path(self):
         return self._path
 
 
-    def __str__(self):
+    def __str__(self) -> str:
         """Override of to-string function
 
         Returns
@@ -93,9 +94,9 @@ def __str__(self):
         """
 
         if self._type == 'file':
-            return '{} {}'.format(self._file_icon, self._name)
+            return f'{self._file_icon} {self._name}'
         else:
-            return '{} {}'.format(self._folder_icon, self._name)
+            return f'{self._folder_icon} {self._name}'
 
 
 
@@ -115,7 +116,7 @@ class FileSelectImplementation(py_cui.ui.MenuImplementation):
     """
 
 
-    def __init__(self, initial_loc, dialog_type, ascii_icons, logger, limit_extensions = [], show_hidden=False):
+    def __init__(self, initial_loc: str, dialog_type: str, ascii_icons, logger, limit_extensions: List[str] = [], show_hidden: bool=False):
         """Initalizer for the file select menu implementation. Includes some logic for getting list of file and folders.
         """
 
@@ -130,7 +131,7 @@ def __init__(self, initial_loc, dialog_type, ascii_icons, logger, limit_extensio
         self.refresh_view()
 
 
-    def refresh_view(self):
+    def refresh_view(self) -> None:
         """Function that refreshes the current list of files and folders in view
         """
 
@@ -181,7 +182,7 @@ class FileSelectElement(py_cui.ui.UIElement, FileSelectImplementation):
         Runs command even if there are no menu items (passes None)
     """
 
-    def __init__(self, root, initial_dir, dialog_type, ascii_icons, title, color, command, renderer, logger, limit_extensions=[]):
+    def __init__(self, root, initial_dir, dialog_type: str, ascii_icons, title, color, command, renderer, logger, limit_extensions: List[str]=[]):
         """Initializer for MenuPopup. Uses MenuImplementation as base
         """
 
@@ -190,15 +191,19 @@ def __init__(self, root, initial_dir, dialog_type, ascii_icons, title, color, co
         self._command              = command
         self._parent_dialog        = root
         self._text_color_rules = [
-            py_cui.colors.ColorRule('\U0001f4c1', py_cui.BLUE_ON_BLACK, py_cui.WHITE_ON_BLUE,'startswith', 'region', [3, 1000], False, logger),
-            py_cui.colors.ColorRule('<DIR>', py_cui.BLUE_ON_BLACK, py_cui.WHITE_ON_BLUE, 'startswith', 'region', [6, 1000], False, logger),
-            py_cui.colors.ColorRule('     ', py_cui.WHITE_ON_BLACK, py_cui.BLACK_ON_WHITE, 'startswith', 'region', [6, 1000], True, logger),
-            py_cui.colors.ColorRule('\U0001f5ce', py_cui.WHITE_ON_BLACK, py_cui.BLACK_ON_WHITE, 'startswith', 'region', [3, 1000], False, logger)
+            py_cui.colors.ColorRule('\U0001f4c1', py_cui.BLUE_ON_BLACK, py_cui.WHITE_ON_BLUE,
+                                    'startswith', 'region', [3, 1000], False, logger),
+            py_cui.colors.ColorRule('<DIR>', py_cui.BLUE_ON_BLACK, py_cui.WHITE_ON_BLUE,
+                                    'startswith', 'region', [6, 1000], False, logger),
+            py_cui.colors.ColorRule('     ', py_cui.WHITE_ON_BLACK, py_cui.BLACK_ON_WHITE,
+                                    'startswith', 'region', [6, 1000], True, logger),
+            py_cui.colors.ColorRule('\U0001f5ce', py_cui.WHITE_ON_BLACK, py_cui.BLACK_ON_WHITE,
+                                    'startswith', 'region', [3, 1000], False, logger)
         ]
         #self._run_command_if_none  = run_command_if_none
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute start position
 
         Returns
@@ -213,7 +218,7 @@ def get_absolute_start_pos(self):
         return start_x, start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute stop position
 
         Returns
@@ -228,7 +233,7 @@ def get_absolute_stop_pos(self):
         return stop_x, stop_y
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base handle key press function
 
         Enter key runs command, Escape key closes menu
@@ -239,27 +244,29 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        #super()._handle_key_press(key_pressed)
         if key_pressed == py_cui.keys.KEY_ENTER:
             old_dir = self._current_dir
-            if os.path.isdir(self.get()._path):
-                self._current_dir = self.get()._path
-                try:
-                    self.refresh_view()
-                except FileNotFoundError:
-                    self._parent_dialog.display_warning('Selected directory does not exist!')
-                    self._current_dir = old_dir
-                    self.refresh_view()
-                except PermissionError:
-                    self._parent_dialog.display_warning('Permission Error Accessing: {} !'.format(self._current_dir))
-                    self._current_dir = old_dir
-                    self.refresh_view()
-                finally:
-                    self.set_title(self._current_dir)
-            elif self._parent_dialog._dialog_type == 'openfile':
-                self._parent_dialog._submit(self.get()._path)
-            else:
-                pass
+            item = self.get()
+            if item is not None:
+                if os.path.isdir(item._path):
+                    self._current_dir = item._path
+                    try:
+                        self.refresh_view()
+                    except FileNotFoundError:
+                        self._parent_dialog.display_warning('Selected directory does not exist!')
+                        self._current_dir = old_dir
+                        self.refresh_view()
+                    except PermissionError:
+                        self._parent_dialog.display_warning(f'Permission Error Accessing: {self._current_dir} !')
+                        self._current_dir = old_dir
+                        self.refresh_view()
+                    finally:
+                        self.set_title(self._current_dir)
+                elif self._parent_dialog._dialog_type == 'openfile':
+                    self._parent_dialog._submit(item._path)
+                else:
+                    pass
 
         viewport_height = self.get_viewport_height()
         if key_pressed == py_cui.keys.KEY_UP_ARROW:
@@ -276,7 +283,7 @@ def _handle_key_press(self, key_pressed):
             self._jump_down(viewport_height)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Overrides base class draw function
         """
 
@@ -327,7 +334,7 @@ def __init__(self, parent_dialog, title, initial_dir, renderer, logger):
         self.update_height_width()
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute start position
 
         Returns
@@ -343,7 +350,7 @@ def get_absolute_start_pos(self):
         return start_x, start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute stop position
 
         Returns
@@ -359,11 +366,11 @@ def get_absolute_stop_pos(self):
         return stop_x, stop_y
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Override of base class. Updates text field variables for form field
         """
 
-        super().update_height_width()
+        py_cui.popups.Popup.update_height_width(self)
         padx, pady              = self.get_padding()
         start_x, start_y        = self.get_start_position()
         height, width           = self.get_absolute_dimensions()
@@ -376,7 +383,7 @@ def update_height_width(self):
         self._viewport_width    = self._cursor_max_right - self._cursor_max_left
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Handles text input for the field. Called by parent
         """
 
@@ -418,7 +425,7 @@ def _handle_key_press(self, key_pressed):
                 self.clear()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Draw function for the field. Called from parent. Essentially the same as a TextboxPopup
         """
 
@@ -470,7 +477,7 @@ def __init__(self, parent_dialog, statusbar_msg, command, button_num, *args):
         self._button_num = button_num
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute start position
 
         Returns
@@ -486,7 +493,7 @@ def get_absolute_start_pos(self):
         return start_x, start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute stop position
 
         Returns
@@ -502,7 +509,7 @@ def get_absolute_stop_pos(self):
         return stop_x, stop_y
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Handles mouse presses 
 
         Parameters
@@ -511,13 +518,16 @@ def _handle_mouse_press(self, x, y):
             x coordinate of click in characters
         y : int
             y coordinate of click in characters
+        mouse_event : int
+            Mouse event keycode of mouse press
         """
 
-        super()._handle_mouse_press(x, y)
-        self.perform_command()
+        super()._handle_mouse_press(x, y, mouse_event)
+        if mouse_event == py_cui.keys.LEFT_MOUSE_CLICK:
+            self.perform_command()
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class, adds ENTER listener that runs the button's command
 
         Parameters
@@ -531,7 +541,7 @@ def _handle_key_press(self, key_pressed):
             self.perform_command()
 
     
-    def perform_command(self):
+    def perform_command(self) -> None:
         if self.command is not None:
             if self._button_num == 1:
                 if self._parent_dialog._dialog_type == 'saveas':
@@ -550,11 +560,11 @@ def perform_command(self):
                 self._parent_dialog._root.close_popup()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class draw function
         """
 
-        super()._draw()
+        #super()._draw()
         self._renderer.set_color_mode(self.get_color())
         self._renderer.draw_border(self, with_title=False)
         button_text_y_pos = self._start_y + int(self._height / 2)
@@ -580,7 +590,7 @@ def __init__(self, parent, *args):
         self._parent = parent
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class, close in parent instead of root
         """
 
@@ -638,7 +648,7 @@ def __init__(self, root, callback, initial_dir, dialog_type, ascii_icons, limit_
         self._filename_input = FileNameInput(self, input_title, '', renderer, logger)
         self._file_dir_select = FileSelectElement(self, initial_dir, dialog_type, ascii_icons, title, color, None, renderer, logger, limit_extensions=limit_extensions)
         self._submit_button = FileDialogButton(self, title, self._submit, 1, '', 'OK', renderer, logger)
-        self._cancel_button = FileDialogButton(self, 'Cancel {}'.format(title), self._root.close_popup, 2, '', 'ESC', renderer, logger)
+        self._cancel_button = FileDialogButton(self, f'Cancel {title}', self._root.close_popup, 2, '', 'ESC', renderer, logger)
 
         # Internal popup used for secondary errors and warnings
         self._internal_popup = None
@@ -649,15 +659,15 @@ def __init__(self, root, callback, initial_dir, dialog_type, ascii_icons, limit_
         self._currently_selected = self._file_dir_select
 
 
-    def _submit(self, output):
+    def _submit(self, output: str) -> None:
         valid, msg = self.output_valid(output)
-        if not valid:
+        if not valid and msg is not None:
             self.display_warning(msg)
         else:
             self._submit_action(output)
 
 
-    def display_warning(self, message):
+    def display_warning(self, message: str) -> None:
         """Helper function for showing internal popup warning message
 
         Parameters
@@ -676,7 +686,7 @@ def display_warning(self, message):
                                                         self._logger)
 
 
-    def output_valid(self, output):
+    def output_valid(self, output) -> Tuple[bool,Optional[str]]:
         if self._dialog_type == 'openfile' and not os.path.isfile(output):
             return False, 'Please select a valid file path!'
         elif self._dialog_type == 'opendir' and not os.path.isdir(output):
@@ -686,7 +696,7 @@ def output_valid(self, output):
         return True, None
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -702,7 +712,7 @@ def get_absolute_start_pos(self):
         return form_start_x, form_start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -718,7 +728,7 @@ def get_absolute_stop_pos(self):
         return form_stop_x, form_stop_y
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Override of base class function
 
         Also updates all form field elements in the form
@@ -734,7 +744,7 @@ def update_height_width(self):
             pass
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed:int) -> None:
         """Override of base class. Here, we handle tabs, enters, and escapes
 
         All other key presses are passed to the currently selected field element
@@ -777,7 +787,7 @@ def _handle_key_press(self, key_pressed):
             self._internal_popup._handle_key_press(key_pressed)
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Override of base class function
 
         Simply enters the appropriate field when mouse is pressed on it
@@ -788,24 +798,25 @@ def _handle_mouse_press(self, x, y):
             Coordinates of the mouse press
         """
 
-        super()._handle_mouse_press(x, y)
+        super()._handle_mouse_press(x, y, mouse_event)
         if self._file_dir_select._contains_position(x, y):
             self._filename_input.set_selected(False)
             self._file_dir_select.set_selected(True)
-            self._file_dir_select._handle_mouse_press(x, y)
+            self._file_dir_select._handle_mouse_press(x, y, mouse_event)
             
         elif self._filename_input._contains_position(x, y):
             self._filename_input.set_selected(True)
             self._file_dir_select.set_selected(False)
-            self._filename_input._handle_mouse_press(x, y)
+            self._filename_input._handle_mouse_press(x, y, mouse_event)
         
         elif self._submit_button._contains_position(x, y):
-            self._submit_button._handle_mouse_press(x, y)
+            self._submit_button._handle_mouse_press(x, y, mouse_event)
+
         elif self._cancel_button._contains_position(x, y):
-            self._cancel_button._handle_mouse_press(x, y)
+            self._cancel_button._handle_mouse_press(x, y, mouse_event)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class.
         
         Here, we only draw a border, and then the individual form elements
@@ -830,4 +841,4 @@ def _draw(self):
             self._internal_popup._draw()
         self._renderer.unset_color_mode(self._color)
 
-#class FileDialogWidget
\ No newline at end of file
+#class FileDialogWidget
diff --git a/py_cui/dialogs/form.py b/py_cui/dialogs/form.py
index 03f8d3d..af641ad 100644
--- a/py_cui/dialogs/form.py
+++ b/py_cui/dialogs/form.py
@@ -1,6 +1,7 @@
 """Form widget for py_cui. Allows for giving user several fillable text fields in one block
 """
 
+from typing import Any, Callable, Dict, List, Optional, Tuple
 import py_cui.ui
 import py_cui.widgets
 import py_cui.popups
@@ -24,7 +25,7 @@ class FormField(py_cui.ui.TextBoxImplementation):
         Toggle for making the field be required
     """
 
-    def __init__(self, fieldname, initial_text, password, required, logger):
+    def __init__(self, fieldname: str, initial_text: str, password: bool, required: bool, logger):
         """Initializer for base FormFields
         """
 
@@ -33,7 +34,7 @@ def __init__(self, fieldname, initial_text, password, required, logger):
         self._required = required
 
 
-    def get_fieldname(self):
+    def get_fieldname(self) -> str:
         """Getter for field name
 
         Returns
@@ -45,7 +46,7 @@ def get_fieldname(self):
         return self._fieldname
 
 
-    def is_valid(self):
+    def is_valid(self) -> Tuple[bool,Optional[str]]:
         """Function that checks if field is valid.
 
         This function can be implemented by subclasses to support different
@@ -61,12 +62,12 @@ def is_valid(self):
 
         msg = None
         if len(self._text) == 0 and self.is_required():
-            msg = 'Field <{}> cannot be empty!'.format(self.get_fieldname())
+            msg = f'Field <{self.get_fieldname()}> cannot be empty!'
 
         return msg is None, msg
 
 
-    def is_required(self):
+    def is_required(self) -> bool:
         """Checks if field is required
         
         Returns
@@ -89,7 +90,7 @@ class FormFieldElement(py_cui.ui.UIElement, FormField):
         The parent UI Element that contains the form element
     """
 
-    def __init__(self, parent_form, field_index, field, init_text, passwd, required, renderer, logger):
+    def __init__(self, parent_form, field_index: int, field, init_text: str, passwd: bool, required: bool, renderer: 'py_cui.renderer.Renderer', logger):
         """Initializer for the FormFieldElement class
         """
 
@@ -104,7 +105,7 @@ def __init__(self, parent_form, field_index, field, init_text, passwd, required,
         self.update_height_width()
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute start position
 
         Returns
@@ -121,7 +122,7 @@ def get_absolute_start_pos(self):
         return field_start_x, field_start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base function. Uses the parent element do compute stop position
 
         Returns
@@ -139,7 +140,7 @@ def get_absolute_stop_pos(self):
         return field_stop_x, field_stop_y
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Override of base class. Updates text field variables for form field
         """
 
@@ -155,7 +156,7 @@ def update_height_width(self):
         self._viewport_width    = self._cursor_max_right - self._cursor_max_left
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Handles text input for the field. Called by parent
         """
 
@@ -175,7 +176,7 @@ def _handle_key_press(self, key_pressed):
             self._insert_char(key_pressed)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Draw function for the field. Called from parent. Essentially the same as a TextboxPopup
         """
 
@@ -217,7 +218,7 @@ class FormImplementation(py_cui.ui.UIImplementation):
         Function fired when submit is called
     """
 
-    def __init__(self, field_implementations, required_fields, logger):
+    def __init__(self, field_implementations: List['FormField'], required_fields: List[str], logger):
         """Initializer for the FormImplemnentation class
         """
 
@@ -226,10 +227,10 @@ def __init__(self, field_implementations, required_fields, logger):
         self._required_fields = required_fields
         
         self._selected_form_index = 0
-        self._on_submit_action = None
+        self._on_submit_action: Optional[Callable[[],Any]] = None
 
 
-    def get_selected_form_index(self):
+    def get_selected_form_index(self) -> int:
         """Getter for selected form index
 
         Returns
@@ -240,7 +241,7 @@ def get_selected_form_index(self):
 
         return self._selected_form_index
 
-    def set_selected_form_index(self, form_index):
+    def set_selected_form_index(self, form_index: int) -> None:
         """Setter for selected form index
 
         Parameters
@@ -252,7 +253,7 @@ def set_selected_form_index(self, form_index):
         self._selected_form_index = form_index
 
 
-    def set_on_submit_action(self, on_submit_action):
+    def set_on_submit_action(self, on_submit_action: Callable[[],Any]):
         """Setter for callback on submit
 
         Parameters
@@ -264,7 +265,7 @@ def set_on_submit_action(self, on_submit_action):
         self._on_submit_action = on_submit_action
 
 
-    def jump_to_next_field(self):
+    def jump_to_next_field(self) -> None:
         """Function used to jump between form fields
         """
 
@@ -274,7 +275,7 @@ def jump_to_next_field(self):
             self.set_selected_form_index(0)
 
 
-    def is_submission_valid(self):
+    def is_submission_valid(self) -> Tuple[bool,Optional[str]]:
         """Function that checks if all fields are filled out correctly
 
         Returns
@@ -292,7 +293,7 @@ def is_submission_valid(self):
         return True, None
 
 
-    def get(self):
+    def get(self) -> Dict[str,str]:
         """Gets values entered into field as a dictionary
 
         Returns
@@ -323,7 +324,7 @@ class InternalFormPopup(py_cui.popups.MessagePopup):
         The parent form popup that spawned the message popup
     """
 
-    def __init__(self, parent, *args):
+    def __init__(self, parent: 'FormPopup', *args):
         """Initializer for Internal form Popup
         """
 
@@ -331,7 +332,7 @@ def __init__(self, parent, *args):
         self._parent = parent
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class, close in parent instead of root
         """
 
@@ -360,7 +361,7 @@ def __init__(self, root, fields, passwd_fields, required_fields, fields_init_tex
 
         py_cui.popups.Popup.__init__(self, root, title, '', color, renderer, logger)
 
-        self._form_fields = []
+        self._form_fields: List['FormFieldElement'] = []
         for i, field in enumerate(fields):
             init_text = ''
             if field in fields_init_text:
@@ -379,7 +380,7 @@ def __init__(self, root, fields, passwd_fields, required_fields, fields_init_tex
         self._internal_popup = None
 
 
-    def get_num_fields(self):
+    def get_num_fields(self) -> int:
         """Getter for number of fields
 
         Returns
@@ -391,7 +392,7 @@ def get_num_fields(self):
         return self._num_fields
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -417,7 +418,7 @@ def get_absolute_start_pos(self):
         return form_start_x, form_start_y
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -443,7 +444,7 @@ def get_absolute_stop_pos(self):
         return form_stop_x, form_stop_y
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Override of base class function
 
         Also updates all form field elements in the form
@@ -457,7 +458,7 @@ def update_height_width(self):
             pass
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class. Here, we handle tabs, enters, and escapes
 
         All other key presses are passed to the currently selected field element
@@ -477,12 +478,13 @@ def _handle_key_press(self, key_pressed):
                 valid, err_msg = self.is_submission_valid()
                 if valid:
                     self._root.close_popup()
-                    self._on_submit_action(self.get())
+                    if self._on_submit_action is not None:
+                        self._on_submit_action(self.get()) 
                 else:
                     self._internal_popup = InternalFormPopup(self,
                                                              self._root, 
                                                              err_msg, 
-                                                             'Required fields: {}'.format(str(self._required_fields)),
+                                                             f'Required fields: {str(self._required_fields)}',
                                                              py_cui.YELLOW_ON_BLACK, 
                                                              self._renderer, 
                                                              self._logger)
@@ -495,7 +497,7 @@ def _handle_key_press(self, key_pressed):
             self._internal_popup._handle_key_press(key_pressed)
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Override of base class function
 
         Simply enters the appropriate field when mouse is pressed on it
@@ -506,7 +508,7 @@ def _handle_mouse_press(self, x, y):
             Coordinates of the mouse press
         """
 
-        super()._handle_mouse_press(x, y)
+        py_cui.popups.Popup._handle_mouse_press(self, x, y, mouse_event)
         for i, field in enumerate(self._form_fields):
             if field._contains_position(x, y):
                 self._form_fields[self.get_selected_form_index()].set_selected(False)
@@ -515,7 +517,7 @@ def _handle_mouse_press(self, x, y):
                 break
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class.
         
         Here, we only draw a border, and then the individual form elements
@@ -532,4 +534,4 @@ def _draw(self):
         self._form_fields[self.get_selected_form_index()]._draw()
 
         if self._internal_popup is not None:
-            self._internal_popup._draw()
\ No newline at end of file
+            self._internal_popup._draw()
diff --git a/py_cui/grid.py b/py_cui/grid.py
index a3089d4..640bff6 100644
--- a/py_cui/grid.py
+++ b/py_cui/grid.py
@@ -7,6 +7,7 @@
 # Created:   12-Aug-2019
 
 
+from typing import Tuple
 import py_cui
 
 
@@ -23,12 +24,14 @@ class Grid:
         The number of additional characters found by height mod rows and width mod columns
     _row_height, _column_width : int
         The number of characters in a single grid row, column
+    _title_bar_offset : int
+        Title bar row offset. Defaults to 1. Set to 0 if title bar is hidden.
     _logger : py_cui.debug.PyCUILogger
         logger object for maintaining debug messages
     """
 
 
-    def __init__(self, num_rows, num_columns, height, width, logger):
+    def __init__(self, num_rows: int, num_columns: int, height: int, width: int, logger: 'py_cui.debug.PyCUILogger'):
         """Constructor for the Grid class
 
         Parameters
@@ -51,10 +54,11 @@ def __init__(self, num_rows, num_columns, height, width, logger):
         self._offset_y      = self._height  % self._num_rows    - 1
         self._row_height    = int(self._height   / self._num_rows)
         self._column_width  = int(self._width    / self._num_columns)
+        self._title_bar_offset = 1
         self._logger        = logger
 
 
-    def get_dimensions(self):
+    def get_dimensions(self) -> Tuple[int,int]:
         """Gets dimensions in rows/columns
 
         Returns
@@ -68,7 +72,7 @@ def get_dimensions(self):
         return self._num_rows, self._num_columns
 
 
-    def get_dimensions_absolute(self):
+    def get_dimensions_absolute(self) -> Tuple[int,int]:
         """Gets dimensions of grid in terminal characters
 
         Returns
@@ -82,7 +86,7 @@ def get_dimensions_absolute(self):
         return self._height, self._width
 
 
-    def get_offsets(self):
+    def get_offsets(self) -> Tuple[int,int]:
         """Gets leftover characters for x and y
 
         Returns
@@ -96,7 +100,7 @@ def get_offsets(self):
         return self._offset_x, self._offset_y
 
 
-    def get_cell_dimensions(self):
+    def get_cell_dimensions(self) -> Tuple[int,int]:
         """Gets size in characters of single (row, column) cell location
 
         Returns
@@ -110,7 +114,7 @@ def get_cell_dimensions(self):
         return self._row_height, self._column_width
 
 
-    def set_num_rows(self, num_rows):
+    def set_num_rows(self, num_rows: int) -> None:
         """Sets the grid row size
         
         Parameters
@@ -131,7 +135,7 @@ def set_num_rows(self, num_rows):
         self._row_height = int(self._height / self._num_rows)
 
 
-    def set_num_cols(self, num_columns):
+    def set_num_cols(self, num_columns: int) -> None:
         """Sets the grid column size
         
         Parameters
@@ -153,7 +157,7 @@ def set_num_cols(self, num_columns):
         self._column_width  = int(self._width / self._num_columns)
 
 
-    def update_grid_height_width(self, height, width):
+    def update_grid_height_width(self, height: int, width: int):
         """Update grid height and width. Allows for on-the-fly size editing
         
         Parameters
@@ -184,4 +188,5 @@ def update_grid_height_width(self, height, width):
         self._column_width   = int(self._width    / self._num_columns)
         self._offset_x       = self._width    % self._num_columns
         self._offset_y       = self._height   % self._num_rows
-        self._logger.info('Updated grid. Cell dims: {}x{}, Offsets {},{}'.format(self._row_height, self._column_width, self._offset_x, self._offset_y))
\ No newline at end of file
+        self._logger.debug(f'Updated grid. Cell dims: {self._row_height}x{self._column_width}, \
+                             Offsets {self._offset_x},{self._offset_y}')
diff --git a/py_cui/keys.py b/py_cui/keys.py
index f85901d..af6fb00 100644
--- a/py_cui/keys.py
+++ b/py_cui/keys.py
@@ -7,10 +7,11 @@
 
 from sys import platform
 import curses
+from typing import Optional
 
 # Some simple helper functions
 
-def get_ascii_from_char(char):
+def get_ascii_from_char(char: str) -> int:
     """Function that converts ascii code to character
 
     Parameters
@@ -27,7 +28,7 @@ def get_ascii_from_char(char):
     return ord(char)
 
 
-def get_char_from_ascii(key_num):
+def get_char_from_ascii(key_num: int) -> Optional[str]:
     """Function that converts a character to an ascii code
 
     Parameters
@@ -40,7 +41,10 @@ def get_char_from_ascii(key_num):
     char : character
         character converted from ascii
     """
-    
+
+    if key_num is None:
+        return None
+
     return chr(key_num)
 
 
@@ -52,6 +56,24 @@ def get_char_from_ascii(key_num):
 KEY_DELETE      = curses.KEY_DC
 KEY_TAB         = get_ascii_from_char('\t')
 
+# Pressing backspace returns 8 on windows?
+if platform == 'win32':
+    KEY_BACKSPACE   = 8
+# Adds support for 'delete/backspace' key on OSX
+elif platform == 'darwin':
+    KEY_BACKSPACE   = 127
+else:
+    KEY_BACKSPACE   = curses.KEY_BACKSPACE
+
+SPECIAL_KEYS = [KEY_ENTER, KEY_ESCAPE, KEY_SPACE, KEY_DELETE, KEY_TAB, KEY_BACKSPACE]
+
+
+# Page navigation keys
+KEY_PAGE_UP     = curses.KEY_PPAGE
+KEY_PAGE_DOWN   = curses.KEY_NPAGE
+KEY_HOME        = curses.KEY_HOME
+KEY_END         = curses.KEY_END
+
 # Arrow Keys
 KEY_UP_ARROW    = curses.KEY_UP
 KEY_DOWN_ARROW  = curses.KEY_DOWN
@@ -59,6 +81,9 @@ def get_char_from_ascii(key_num):
 KEY_RIGHT_ARROW = curses.KEY_RIGHT
 
 
+ARROW_KEYS = [KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW]
+
+
 if platform == 'linux' or platform == 'darwin':
     
     # Modified arrow keys
@@ -85,14 +110,8 @@ def get_char_from_ascii(key_num):
     KEY_CTRL_DOWN   = 481
 
 
-ARROW_KEYS = [KEY_UP_ARROW, KEY_DOWN_ARROW, KEY_LEFT_ARROW, KEY_RIGHT_ARROW]
-
-
-# Page navigation keys
-KEY_PAGE_UP     = curses.KEY_PPAGE
-KEY_PAGE_DOWN   = curses.KEY_NPAGE
-KEY_HOME        = curses.KEY_HOME
-KEY_END         = curses.KEY_END
+MODIFIED_ARROW_KEYS = [KEY_SHIFT_LEFT, KEY_SHIFT_RIGHT, KEY_SHIFT_UP, KEY_SHIFT_DOWN,
+                       KEY_CTRL_LEFT, KEY_CTRL_RIGHT, KEY_CTRL_UP, KEY_CTRL_DOWN]
 
 
 # Standard letter keys
@@ -158,7 +177,7 @@ def get_char_from_ascii(key_num):
 LETTER_KEYS = UPPERCASE_LETTER_KEYS + LOWERCASE_LETTER_KEYS
 
 
-# Control Keys
+# Control-modified Keys
 KEY_CTRL_A      = 1
 KEY_CTRL_B      = 2
 KEY_CTRL_C      = 3
@@ -220,6 +239,11 @@ def get_char_from_ascii(key_num):
 KEY_ALT_Y       = 121 + _ALT_MODIFIER
 KEY_ALT_Z       = 122 + _ALT_MODIFIER
 
+ALT_MODIFIED_LETTERS = [_alt_mod_key + _ALT_MODIFIER for _alt_mod_key in range(97, 123)]
+
+# List of all ctrl / alt modified key codes supported by py_cui
+MODIFIED_LETTER_KEYS = CTRL_MODIFIED_LETTERS + ALT_MODIFIED_LETTERS
+
 # Function keys
 KEY_F0          = curses.KEY_F0
 KEY_F1          = curses.KEY_F1
@@ -235,6 +259,9 @@ def get_char_from_ascii(key_num):
 KEY_F11         = curses.KEY_F11
 KEY_F12         = curses.KEY_F12
 
+FUNCTION_KEYS = [KEY_F0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5,
+                 KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12]
+
 
 # Number keys
 KEY_0 = get_ascii_from_char('0')
@@ -252,12 +279,32 @@ def get_char_from_ascii(key_num):
 NUMBER_KEYS = [KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9]
 ALPHANUMERIC_KEYS = LETTER_KEYS + NUMBER_KEYS
 
-# Pressing backspace returns 8 on windows?
-if platform == 'win32':
-    KEY_BACKSPACE   = 8
-# Adds support for 'delete/backspace' key on OSX
-elif platform == 'darwin':
-    KEY_BACKSPACE   = 127
-else:
-    KEY_BACKSPACE   = curses.KEY_BACKSPACE
+
+# List of all key events supported by py_cui
+ALL_KEYS = ALPHANUMERIC_KEYS + FUNCTION_KEYS + MODIFIED_LETTER_KEYS + SPECIAL_KEYS + ARROW_KEYS + MODIFIED_ARROW_KEYS
+
+
+# Mouse click events
+LEFT_MOUSE_CLICK        = curses.BUTTON1_CLICKED
+LEFT_MOUSE_DBL_CLICK    = curses.BUTTON1_DOUBLE_CLICKED
+LEFT_MOUSE_TRPL_CLICK   = curses.BUTTON1_TRIPLE_CLICKED
+LEFT_MOUSE_PRESSED      = curses.BUTTON1_PRESSED
+LEFT_MOUSE_RELEASED     = curses.BUTTON1_RELEASED
+
+MIDDLE_MOUSE_CLICK      = curses.BUTTON2_CLICKED
+MIDDLE_MOUSE_DBL_CLICK  = curses.BUTTON2_DOUBLE_CLICKED
+MIDDLE_MOUSE_TRPL_CLICK = curses.BUTTON2_TRIPLE_CLICKED
+MIDDLE_MOUSE_PRESSED    = curses.BUTTON2_PRESSED
+MIDDLE_MOUSE_RELEASED   = curses.BUTTON2_RELEASED
+
+RIGHT_MOUSE_CLICK        = curses.BUTTON3_CLICKED
+RIGHT_MOUSE_DBL_CLICK    = curses.BUTTON3_DOUBLE_CLICKED
+RIGHT_MOUSE_TRPL_CLICK   = curses.BUTTON3_TRIPLE_CLICKED
+RIGHT_MOUSE_PRESSED      = curses.BUTTON3_PRESSED
+RIGHT_MOUSE_RELEASED     = curses.BUTTON3_RELEASED
+
+MOUSE_EVENTS = [LEFT_MOUSE_CLICK, LEFT_MOUSE_DBL_CLICK, LEFT_MOUSE_TRPL_CLICK, LEFT_MOUSE_PRESSED, LEFT_MOUSE_RELEASED,
+                MIDDLE_MOUSE_CLICK, MIDDLE_MOUSE_DBL_CLICK, MIDDLE_MOUSE_TRPL_CLICK, MIDDLE_MOUSE_PRESSED, MIDDLE_MOUSE_RELEASED,
+                RIGHT_MOUSE_CLICK, RIGHT_MOUSE_DBL_CLICK, RIGHT_MOUSE_TRPL_CLICK, RIGHT_MOUSE_PRESSED, RIGHT_MOUSE_RELEASED]
+
 
diff --git a/py_cui/popups.py b/py_cui/popups.py
index c2954de..470225f 100644
--- a/py_cui/popups.py
+++ b/py_cui/popups.py
@@ -10,6 +10,7 @@
 import py_cui
 import py_cui.ui
 import py_cui.errors
+from typing import Tuple
 
 
 class Popup(py_cui.ui.UIElement):
@@ -32,7 +33,7 @@ class Popup(py_cui.ui.UIElement):
     """
 
 
-    def __init__(self, root, title, text, color, renderer, logger):
+    def __init__(self, root: 'py_cui.PyCUI', title: str, text: str, color: int, renderer: 'py_cui.renderer.Renderer', logger):
         """Initializer for main popup class. Calls UIElement intialier, and sets some initial values
         """
 
@@ -56,7 +57,7 @@ def _increment_counter(self):
         pass
 
 
-    def set_text(self, text):
+    def set_text(self, text: str) -> None :
         """Sets popup text (message)
 
         Parameters
@@ -68,7 +69,7 @@ def set_text(self, text):
         self._text = text
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -81,7 +82,7 @@ def get_absolute_start_pos(self):
         return int(root_width / 4), int(root_height / 3)
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Override of base class, computes position based on root dimensions
         
         Returns
@@ -94,7 +95,7 @@ def get_absolute_stop_pos(self):
         return (int(3 * root_width / 4)), (int(2 * root_height / 3))
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Handles key presses when popup is open
 
         By default, only closes popup when Escape is pressed
@@ -109,7 +110,7 @@ def _handle_key_press(self, key_pressed):
             self._root.close_popup()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Function that uses renderer to draw the popup
 
         Can be implemented by subclass. Base draw function will draw the title and text in a bordered box
@@ -144,7 +145,7 @@ def __init__(self, root, title, text, color, renderer, logger):
                             py_cui.keys.KEY_DELETE]
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Draw function for MessagePopup. Calls superclass draw()
         """
 
@@ -168,7 +169,7 @@ def __init__(self, root, title, text, color, command, renderer, logger):
         self._command = command
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int):
         """Handle key press overwrite from superclass
 
         Parameters
@@ -191,7 +192,7 @@ def _handle_key_press(self, key_pressed):
             if self._command is not None:
                 self._command(ret_val)
             else:
-                self._root.show_warning_popup('No Command Specified', 'The textbox popup had no specified command!')
+                self._root.show_warning_popup('No Command Specified', 'The yes/no popup had no specified command!')
 
 
     def _draw(self):
@@ -220,11 +221,11 @@ def __init__(self, root, title, color, command, renderer, password, logger):
         self.update_height_width()
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Need to update all cursor positions on resize
         """
 
-        super().update_height_width()
+        Popup.update_height_width(self)
         padx, pady              = self.get_padding()
         start_x, start_y        = self.get_start_position()
         height, width           = self.get_absolute_dimensions()
@@ -236,7 +237,28 @@ def update_height_width(self):
         self._viewport_width    = self._cursor_max_right - self._cursor_max_left
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event) -> None:
+        """Override of base class function, handles mouse press in menu
+
+        Parameters
+        ----------
+        x, y : int
+            Coordinates of mouse press
+        """
+
+        Popup._handle_mouse_press(self, x, y, mouse_event)
+        if y == self._cursor_y and x >= self._cursor_max_left and x <= self._cursor_max_right:
+            if x <= len(self._text) + self._cursor_max_left:
+                old_text_pos = self._cursor_text_pos
+                old_cursor_x = self._cursor_x
+                self._cursor_x = x
+                self._cursor_text_pos = old_text_pos + (x - old_cursor_x)
+            else:
+                self._cursor_x = self._cursor_max_left + len(self._text)
+                self._cursor_text_pos = len(self._text)
+
+
+    def _handle_key_press(self, key_pressed: int):
         """Override of base handle key press function
 
         Parameters
@@ -245,7 +267,7 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        Popup._handle_key_press(self, key_pressed)
         valid_pressed = False
         if key_pressed == py_cui.keys.KEY_ENTER:
             self._ret_val = self._text
@@ -274,7 +296,7 @@ def _handle_key_press(self, key_pressed):
             self._insert_char(key_pressed)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base draw function
         """
 
@@ -314,7 +336,7 @@ class MenuPopup(Popup, py_cui.ui.MenuImplementation):
         Runs command even if there are no menu items (passes None)
     """
 
-    def __init__(self, root, items, title, color, command, renderer, logger, run_command_if_none):
+    def __init__(self, root: 'py_cui.PyCUI', items, title, color, command, renderer, logger, run_command_if_none):
         """Initializer for MenuPopup. Uses MenuImplementation as base
         """
 
@@ -325,7 +347,33 @@ def __init__(self, root, items, title, color, command, renderer, logger, run_com
         self._run_command_if_none  = run_command_if_none
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event):
+        """Override of base class function, handles mouse press in menu
+
+        Parameters
+        ----------
+        x, y : int
+            Coordinates of mouse press
+        """
+
+        # For either click or double click we want to jump to the clicked-on item
+        if mouse_event == py_cui.keys.LEFT_MOUSE_CLICK or mouse_event == py_cui.keys.LEFT_MOUSE_DBL_CLICK:
+            current = self.get_selected_item_index()
+            viewport_top = self._start_y + self._pady + 1
+
+            if viewport_top <= y and viewport_top + len(self._view_items) - self._top_view >= y:
+                elem_clicked = y - viewport_top + self._top_view
+                self.set_selected_item_index(elem_clicked)
+
+                # For double clicks we also process the menu selection
+                if mouse_event == py_cui.keys.LEFT_MOUSE_DBL_CLICK:
+                    ret_val = self.get()
+                    self._root.close_popup()
+                    self._command(ret_val)
+
+
+
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base handle key press function
 
         Enter key runs command, Escape key closes menu
@@ -336,7 +384,7 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        Popup._handle_key_press(self, key_pressed)
         valid_pressed = False
         if key_pressed == py_cui.keys.KEY_ENTER:
             ret_val = self.get()
@@ -360,7 +408,7 @@ def _handle_key_press(self, key_pressed):
             self._scroll_down(viewport_height)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Overrides base class draw function
         """
 
@@ -369,7 +417,8 @@ def _draw(self):
         self._renderer.set_color_rules([])
         counter = self._pady + 1
         line_counter = 0
-        for line in self._view_items:
+        for item in self._view_items:
+            line = str(item)
             if line_counter < self._top_view:
                 line_counter = line_counter + 1
             else:
@@ -404,13 +453,13 @@ def __init__(self, root, title, message, color, renderer, logger):
         """Initializer for LoadingIconPopup
         """
 
-        super().__init__(root, title, '{} ... \\'.format(message), color, renderer, logger)
+        super().__init__(root, title, f'{message} ... \\', color, renderer, logger)
         self._loading_icons = ['\\', '|', '/', '-']
         self._icon_counter = 0
         self._message = message
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int):
         """Override of base class function.
 
         Loading icon popups cannot be cancelled, so we wish to avoid default behavior
@@ -424,11 +473,11 @@ def _handle_key_press(self, key_pressed):
         pass
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Overrides base draw function
         """
 
-        self._text = '{} ... {}'.format(self._message, self._loading_icons[self._icon_counter])
+        self._text = f'{self._message} ... {self._loading_icons[self._icon_counter]}'
         self._icon_counter = self._icon_counter + 1
         if self._icon_counter == len(self._loading_icons):
             self._icon_counter = 0
@@ -454,14 +503,14 @@ def __init__(self, root, title, num_items, color, renderer, logger):
         """Initializer for LoadingBarPopup
         """
 
-        super().__init__(root, title, '{} (0/{})'.format('-' * num_items, num_items), color, renderer, logger)
+        super().__init__(root, title, f'{"-" * num_items} (0/{num_items})', color, renderer, logger)
         self._num_items          = num_items
         self._loading_icons      = ['\\', '|', '/', '-']
         self._icon_counter       = 0
         self._completed_items    = 0
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int):
         """Override of base class function.
 
         Loading icon popups cannot be cancelled, so we wish to avoid default behavior
@@ -474,14 +523,14 @@ def _handle_key_press(self, key_pressed):
 
         pass
 
-    def _increment_counter(self):
+    def _increment_counter(self) -> None:
         """Function that increments an internal counter
         """
 
         self._completed_items += 1
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base draw function
         """
 
@@ -503,11 +552,8 @@ def _draw(self):
         if self._icon_counter == len(self._loading_icons):
             self._icon_counter = 0
 
-        self.set_text('{}{} ({}/{}) {}'.format( '#' * completed_blocks, 
-                                                '-' * non_completed_blocks, 
-                                                self._completed_items, 
-                                                self._num_items, 
-                                                self._loading_icons[self._icon_counter]))
+        self.set_text(f'{"#" * completed_blocks}{"-" * non_completed_blocks} \
+                       ({self._completed_items}/{self._num_items}) {self._loading_icons[self._icon_counter]}')
         
         # Use Superclass draw after new text is computed
         super()._draw()
diff --git a/py_cui/renderer.py b/py_cui/renderer.py
index 370f570..ac3b78c 100644
--- a/py_cui/renderer.py
+++ b/py_cui/renderer.py
@@ -7,6 +7,8 @@
 import curses
 import py_cui
 import py_cui.colors
+from typing import Dict, List, Union
+
 
 
 class Renderer:
@@ -25,13 +27,13 @@ class Renderer:
         List of currently loaded rules to apply during drawing
     """
 
-    def __init__(self, root, stdscr, logger):
+    def __init__(self, root: 'py_cui.PyCUI', stdscr, logger):
         """Constructor for renderer object
         """
 
         self._root         = root
         self._stdscr       = stdscr
-        self._color_rules  = []
+        self._color_rules: List['py_cui.colors.ColorRule'] = []
         self._logger       = logger
 
         # Define ui_element border characters
@@ -45,7 +47,7 @@ def __init__(self, root, stdscr, logger):
         }
 
 
-    def _set_border_renderer_chars(self, border_char_set):
+    def _set_border_renderer_chars(self, border_char_set: Dict[str,str]) -> None:
         """Function that sets the border characters for ui_elements
 
         Parameters
@@ -62,21 +64,21 @@ def _set_border_renderer_chars(self, border_char_set):
         self._border_characters['VERTICAL'  ] = border_char_set['VERTICAL'  ]
 
 
-    def _set_bold(self):
+    def _set_bold(self) -> None:
         """Sets bold draw mode
         """
 
         self._stdscr.attron(curses.A_BOLD)
 
 
-    def _unset_bold(self):
+    def _unset_bold(self) -> None:
         """Unsets bold draw mode
         """
 
         self._stdscr.attroff(curses.A_BOLD)
 
 
-    def set_color_rules(self, color_rules):
+    def set_color_rules(self, color_rules) -> None:
         """Sets current color rules
 
         Parameters
@@ -88,7 +90,7 @@ def set_color_rules(self, color_rules):
         self._color_rules = color_rules
 
 
-    def set_color_mode(self, color_mode):
+    def set_color_mode(self, color_mode: int) -> None:
         """Sets the output color mode
 
         Parameters
@@ -100,7 +102,7 @@ def set_color_mode(self, color_mode):
         self._stdscr.attron(curses.color_pair(color_mode))
 
 
-    def unset_color_mode(self, color_mode):
+    def unset_color_mode(self, color_mode: int) -> None:
         """Unsets the output color mode
 
         Parameters
@@ -112,7 +114,7 @@ def unset_color_mode(self, color_mode):
         self._stdscr.attroff(curses.color_pair(color_mode))
 
 
-    def reset_cursor(self, ui_element, fill=True):
+    def reset_cursor(self, ui_element: 'py_cui.ui.UIElement', fill: bool=True) -> None:
         """Positions the cursor at the bottom right of the selected element
 
         Parameters
@@ -139,7 +141,7 @@ def reset_cursor(self, ui_element, fill=True):
             self._stdscr.move(0,0)
 
 
-    def draw_cursor(self, cursor_y, cursor_x):
+    def draw_cursor(self, cursor_y: int, cursor_x: int) -> None:
         """Draws the cursor at a particular location
 
         Parameters
@@ -151,7 +153,7 @@ def draw_cursor(self, cursor_y, cursor_x):
         self._stdscr.move(cursor_y, cursor_x)
 
 
-    def draw_border(self, ui_element, fill=True, with_title=True):
+    def draw_border(self, ui_element: 'py_cui.ui.UIElement', fill: bool=True, with_title: bool=True) -> None:
         """Draws ascii border around ui element
 
         Parameters
@@ -193,7 +195,7 @@ def draw_border(self, ui_element, fill=True, with_title=True):
             self._unset_bold()
 
 
-    def _draw_border_top(self, ui_element, y, with_title):
+    def _draw_border_top(self, ui_element:'py_cui.ui.UIElement', y: int, with_title: bool) -> None:
         """Internal function for drawing top of border
 
         Parameters
@@ -212,20 +214,20 @@ def _draw_border_top(self, ui_element, y, with_title):
         title         = ui_element.get_title()
 
         if not with_title or (len(title) + 4 >= width - 2 * padx):
-            render_text = '{}{}{}'.format(  self._border_characters['UP_LEFT'],
-                                            self._border_characters['HORIZONTAL'] * (width - 2 - 2 * padx),
-                                            self._border_characters['UP_RIGHT'])
+            render_text = f'{self._border_characters["UP_LEFT"]}' \
+                          f'{self._border_characters["HORIZONTAL"] * (width - 2 - 2 * padx)}' \
+                          f'{self._border_characters["UP_RIGHT"]}'
+
             self._stdscr.addstr(y, start_x + padx, render_text)
         else:
-            render_text = '{}{} {} {}{}'.format(self._border_characters['UP_LEFT'],
-                                                2 * self._border_characters['HORIZONTAL'],
-                                                title,
-                                                self._border_characters['HORIZONTAL'] * (width - 6 - 2 * padx - len(title)),
-                                                self._border_characters['UP_RIGHT'])
+            render_text = f'{self._border_characters["UP_LEFT"]}{2 * self._border_characters["HORIZONTAL"]}' \
+                          f' {title} {self._border_characters["HORIZONTAL"] * (width - 6 - 2 * padx - len(title))}' \
+                          f'{self._border_characters["UP_RIGHT"]}'
+
             self._stdscr.addstr(y, start_x + padx, render_text)
 
 
-    def _draw_border_bottom(self, ui_element, y):
+    def _draw_border_bottom(self, ui_element: 'py_cui.ui.UIElement', y: int) -> None:
         """Internal function for drawing bottom of border
 
         Parameters
@@ -240,13 +242,13 @@ def _draw_border_bottom(self, ui_element, y):
         start_x, _    = ui_element.get_start_position()
         _, width      = ui_element.get_absolute_dimensions()
 
-        render_text = '{}{}{}'.format(  self._border_characters['DOWN_LEFT'],
-                                        self._border_characters['HORIZONTAL'] * (width - 2 - 2 * padx),
-                                        self._border_characters['DOWN_RIGHT'])
+        render_text = f'{self._border_characters["DOWN_LEFT"]}' \
+                      f'{self._border_characters["HORIZONTAL"] * (width - 2 - 2 * padx)}' \
+                      f'{self._border_characters["DOWN_RIGHT"]}'
         self._stdscr.addstr(y, start_x + padx, render_text)
 
 
-    def _draw_blank_row(self, ui_element, y):
+    def _draw_blank_row(self, ui_element: 'py_cui.ui.UIElement', y: int) -> None:
         """Internal function for drawing a blank row
 
         Parameters
@@ -261,14 +263,14 @@ def _draw_blank_row(self, ui_element, y):
         start_x, _    = ui_element.get_start_position()
         _, width      = ui_element.get_absolute_dimensions()
 
-        render_text = '{}{}{}'.format(  self._border_characters['VERTICAL'],
-                                        ' ' * (width - 2 - 2 * padx),
-                                        self._border_characters['VERTICAL'])
+        render_text = f'{self._border_characters["VERTICAL"]}' \
+                      f'{" " * (width - 2 - 2 * padx)}' \
+                      f'{self._border_characters["VERTICAL"]}'
         
         self._stdscr.addstr(y, start_x + padx, render_text)
 
 
-    def _get_render_text(self, ui_element, line, centered, bordered, selected, start_pos):
+    def _get_render_text(self, ui_element: 'py_cui.ui.UIElement', line: str, centered: bool, bordered: bool, selected, start_pos:int) -> List[List[Union[int,str]]]:
         """Internal function that computes the scope of the text that should be drawn
 
         Parameters
@@ -286,7 +288,7 @@ def _get_render_text(self, ui_element, line, centered, bordered, selected, start
 
         Returns
         -------
-        render_text : str
+        render_text : str # to be checked, returns a List of [int,str]
             The text shortened to fit within given space
         """
 
@@ -300,19 +302,17 @@ def _get_render_text(self, ui_element, line, centered, bordered, selected, start
 
         if len(line) - start_pos < render_text_length:
             if centered:
-                render_text = '{}'.format(  line[start_pos:].center(render_text_length,
-                                            ' '))
+                render_text = f'{line[start_pos:].center(render_text_length, " ")}'
             else:
-                render_text = '{}{}'.format(line[start_pos:],
-                                            ' ' * (render_text_length - len(line[start_pos:])))
+                render_text = f'{line[start_pos:]}{" " * (render_text_length - len(line[start_pos:]))}'
         else:
             render_text = line[start_pos:start_pos + render_text_length]
 
-        render_text_fragments = self._generate_text_color_fragments(ui_element, line, render_text, selected)
+        render_text_fragments = self._generate_text_color_fragments(ui_element, line, render_text, selected) 
         return render_text_fragments
 
 
-    def _generate_text_color_fragments(self, ui_element, line, render_text, selected):
+    def _generate_text_color_fragments(self, ui_element: 'py_cui.ui.UIElement', line: str, render_text: str, selected) -> List[List[Union[int,str]]]:
         """Function that applies color rules to text, dividing them if match is found
 
         Parameters
@@ -329,6 +329,7 @@ def _generate_text_color_fragments(self, ui_element, line, render_text, selected
         fragments : list of [int, str]
             list of text - color code combinations to write
         """
+        fragments: List[List[Union[int,str]]]
 
         if selected: 
             fragments = [[render_text, ui_element.get_selected_color()]]
@@ -343,7 +344,7 @@ def _generate_text_color_fragments(self, ui_element, line, render_text, selected
         return fragments
 
 
-    def draw_text(self, ui_element, line, y, centered = False, bordered = True, selected = False, start_pos = 0):
+    def draw_text(self, ui_element: 'py_cui.ui.UIElement', line: str, y: int, centered: bool = False, bordered: bool = True, selected: bool = False, start_pos: int = 0):
         """Function that draws ui_element text.
 
         Parameters
@@ -386,19 +387,21 @@ def draw_text(self, ui_element, line, y, centered = False, bordered = True, sele
 
         # Each text elem is a list with [text, color]
         for text_elem in render_text:
-            self.set_color_mode(text_elem[1])
+            if isinstance(text_elem[0],str) and isinstance(text_elem[1],int):
+                text_elem[0] = text_elem[0].replace(chr(0), "")
+                self.set_color_mode(text_elem[1])
 
-            # BLACK_ON_WHITE + BOLD is unreadable on windows terminals
-            if selected and text_elem[1] != py_cui.BLACK_ON_WHITE:
-                self._set_bold()
+                # BLACK_ON_WHITE + BOLD is unreadable on windows terminals
+                if selected and text_elem[1] != py_cui.BLACK_ON_WHITE:
+                    self._set_bold()
 
-            self._stdscr.addstr(y, current_start_x, text_elem[0])
-            current_start_x = current_start_x + len(text_elem[0])
+                self._stdscr.addstr(y, current_start_x, text_elem[0])
+                current_start_x = current_start_x + len(text_elem[0])
 
-            if selected and text_elem[1] != py_cui.BLACK_ON_WHITE:
-                self._unset_bold()
-            
-            self.unset_color_mode(text_elem[1])
+                if selected and text_elem[1] != py_cui.BLACK_ON_WHITE:
+                    self._unset_bold()
+                
+                self.unset_color_mode(text_elem[1])
 
         if ui_element.is_selected():
             self._set_bold()
diff --git a/py_cui/statusbar.py b/py_cui/statusbar.py
index 6b5fb89..eeb62fc 100644
--- a/py_cui/statusbar.py
+++ b/py_cui/statusbar.py
@@ -5,7 +5,7 @@
 
 # Author:    Jakub Wlodek
 # Created:   12-Aug-2019
-
+import py_cui
 
 class StatusBar:
     """Very simple class representing a status bar
@@ -16,19 +16,26 @@ class StatusBar:
         status bar text
     color : py_cui.COLOR
         color to display the statusbar
+    root : py_cui.PyCUI
+        Main PyCUI object reference
+    is_title_bar : bool
+        Is the StatusBar displayed on the top of the grid
     """
 
-    def __init__(self, text, color):
+    def __init__(self, text: str, color: int, root: 'py_cui.PyCUI', is_title_bar: bool=False):
         """Initializer for statusbar
         """
 
         self.__text = text
         self.__color = color
+        self.__height = 1
+        self.__root = root
+        self.__is_title_bar = is_title_bar
 
 
-    def get_color(self):
+    def get_color(self) -> int:
         """Getter for status bar color
-        
+
         Returns
         -------
         color : int
@@ -38,8 +45,8 @@ def get_color(self):
         return self.__color
 
 
-    def get_text(self):
-        """Getter for stattus bar text
+    def get_text(self) -> str:
+        """Getter for status bar text
 
         Returns
         -------
@@ -50,7 +57,7 @@ def get_text(self):
         return self.__text
 
 
-    def set_color(self, color):
+    def set_color(self, color) -> None:
         """Setter for statusbar color
 
         Parameters
@@ -62,7 +69,7 @@ def set_color(self, color):
         self.__color = color
 
 
-    def set_text(self, text):
+    def set_text(self, text: str) -> None :
         """Sets the statusbar text
 
         Parameters
@@ -71,4 +78,34 @@ def set_text(self, text):
             New statusbar text
         """
 
-        self.__text = text
\ No newline at end of file
+        self.__text = text
+
+    def get_height(self) -> int :
+        """Getter for status bar height in row
+
+        Returns
+        -------
+        height : int
+            The statusbar height in row
+        """
+
+        return self.__height
+
+    def show(self) -> None:
+        """Sets the status bar height to 1"""
+
+        self.__height = 1
+        self._refresh_root_size()
+
+    def hide(self) -> None:
+        """Sets the status bar height to 0"""
+
+        self.__height = 0
+        self._refresh_root_size()
+
+    def _refresh_root_size(self) -> None:
+        """Resets the grid's title bar offset if needed and calls a UI size update."""
+
+        if self.__is_title_bar:
+            self.__root._grid._title_bar_offset = self.__height
+        self.__root._refresh_height_width()
diff --git a/py_cui/ui.py b/py_cui/ui.py
index b63b5a7..66445a0 100644
--- a/py_cui/ui.py
+++ b/py_cui/ui.py
@@ -11,6 +11,9 @@
 import py_cui.errors
 import py_cui.colors
 
+import inspect
+from typing import Any, List, Optional, Tuple, Callable
+
 
 class UIElement:
     """Base class for all UI elements. Extended by base widget and popup classes.
@@ -67,28 +70,27 @@ def __init__(self, id, title, renderer, logger):
         self._border_color              = self._color
         self._focus_border_color        = self._color
         self._selected_color            = self._color
-        self._mouse_press_handler       = None
         self._selected                  = False
         self._renderer                  = renderer
         self._logger                    = logger
         self._help_text                 = ''
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Must be implemented by subclass, computes the absolute coords of upper-left corner
         """
 
         raise NotImplementedError
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Must be implemented by subclass, computes the absolute coords of bottom-right corner
         """
 
         raise NotImplementedError
 
 
-    def get_absolute_dimensions(self):
+    def get_absolute_dimensions(self) -> Tuple[int,int]:
         """Gets dimensions of element in terminal characters
 
         Returns
@@ -101,7 +103,7 @@ def get_absolute_dimensions(self):
         return (stop_y - start_y), (stop_x - start_x)
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Function that refreshes position and dimensons on resize.
 
         If necessary, make sure required widget attributes updated here as well.
@@ -112,7 +114,7 @@ def update_height_width(self):
         self._height,  self._width    = self.get_absolute_dimensions()
 
 
-    def get_viewport_height(self):
+    def get_viewport_height(self) -> int:
         """Gets the height of the element viewport (height minus padding and borders)
 
         Returns
@@ -124,7 +126,7 @@ def get_viewport_height(self):
         return self._height - (2 * self._pady) - 3
 
 
-    def get_id(self):
+    def get_id(self) -> int:
         """Gets the element ID
 
         Returns
@@ -136,7 +138,7 @@ def get_id(self):
         return self._id
 
 
-    def get_title(self):
+    def get_title(self) -> str:
         """Getter for ui element title
 
         Returns
@@ -148,7 +150,7 @@ def get_title(self):
         return self._title
 
 
-    def get_padding(self):
+    def get_padding(self) -> Tuple[int,int]:
         """Gets ui element padding on in characters
 
         Returns
@@ -160,7 +162,7 @@ def get_padding(self):
         return self._padx, self._pady
 
 
-    def get_start_position(self):
+    def get_start_position(self) -> Tuple[int,int]:
         """Gets coords of upper left corner
 
         Returns
@@ -172,7 +174,7 @@ def get_start_position(self):
         return self._start_x, self._start_y
 
 
-    def get_stop_position(self):
+    def get_stop_position(self) -> Tuple[int,int]:
         """Gets coords of lower right corner
 
         Returns
@@ -184,7 +186,7 @@ def get_stop_position(self):
         return self._stop_x, self._stop_y
 
 
-    def get_color(self):
+    def get_color(self) -> int:
         """Gets current element color
 
         Returns
@@ -196,7 +198,7 @@ def get_color(self):
         return self._color
 
 
-    def get_border_color(self):
+    def get_border_color(self) -> int:
         """Gets current element border color
 
         Returns
@@ -211,7 +213,7 @@ def get_border_color(self):
             return self._border_color
 
 
-    def get_selected_color(self):
+    def get_selected_color(self) -> int:
         """Gets current selected item color
 
         Returns
@@ -223,7 +225,7 @@ def get_selected_color(self):
         return self._selected_color
 
 
-    def is_selected(self):
+    def is_selected(self) -> bool:
         """Get selected status
 
         Returns
@@ -235,7 +237,7 @@ def is_selected(self):
         return self._selected
 
 
-    def get_renderer(self):
+    def get_renderer(self) -> 'py_cui.renderer.Renderer':
         """Gets reference to renderer object
 
         Returns
@@ -247,7 +249,7 @@ def get_renderer(self):
         return self._renderer
 
 
-    def get_help_text(self):
+    def get_help_text(self) -> str:
         """Returns current help text
 
         Returns
@@ -259,7 +261,7 @@ def get_help_text(self):
         return self._help_text
 
 
-    def set_title(self, title):
+    def set_title(self, title: str):
         """Function that sets the widget title.
 
         Parameters
@@ -271,7 +273,7 @@ def set_title(self, title):
         self._title = title
 
 
-    def set_color(self, color):
+    def set_color(self, color: int) -> None:
         """Sets element default color
 
         Parameters
@@ -289,7 +291,7 @@ def set_color(self, color):
         self._color = color
 
 
-    def set_border_color(self, color):
+    def set_border_color(self, color: int) -> None:
         """Sets element border color
 
         Parameters
@@ -301,7 +303,7 @@ def set_border_color(self, color):
         self._border_color = color
 
 
-    def set_focus_border_color(self, color):
+    def set_focus_border_color(self, color: int) -> None:
         """Sets element border color if the current element
         is focused
 
@@ -314,7 +316,7 @@ def set_focus_border_color(self, color):
         self._focus_border_color = color
 
 
-    def set_selected_color(self, color):
+    def set_selected_color(self, color: int) -> None:
         """Sets element sected color
 
         Parameters
@@ -326,7 +328,7 @@ def set_selected_color(self, color):
         self._selected_color = color
 
 
-    def set_selected(self, selected):
+    def set_selected(self, selected: bool) -> None:
         """Marks the UI element as selected or not selected
 
         Parameters
@@ -338,7 +340,7 @@ def set_selected(self, selected):
         self._selected = selected
 
 
-    def set_help_text(self, help_text):
+    def set_help_text(self, help_text: str) -> None:
         """Sets status bar help text
 
         Parameters
@@ -350,7 +352,7 @@ def set_help_text(self, help_text):
         self._help_text = help_text
 
 
-    def set_focus_text(self, focus_text):
+    def set_focus_text(self, focus_text: str) -> None:
         """Sets status bar focus text. Legacy function, overridden by set_focus_text
 
         Parameters
@@ -369,19 +371,7 @@ def _handle_key_press(self, key_pressed):
         raise NotImplementedError
 
 
-    def add_mouse_press_handler(self, mouse_press_handler_func):
-        """Sets a mouse press handler function
-
-        Parameters
-        ----------
-        mouse_press_handler_func : function / lambda function
-            Function that takes 2 parameters: x and y of a mouse press. Executes when mouse pressed and element is selected
-        """
-
-        self._mouse_press_handler = mouse_press_handler_func
-
-
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x, y, mouse_event):
         """Can be implemented by subclass. Used to handle mouse presses
 
         Parameters
@@ -390,8 +380,7 @@ def _handle_mouse_press(self, x, y):
             Coordinates of the mouse press event.
         """
 
-        if self._mouse_press_handler is not None:
-            self._mouse_press_handler(x, y)
+        pass
 
 
     def _draw(self):
@@ -401,7 +390,7 @@ def _draw(self):
         raise NotImplementedError
 
 
-    def _assign_renderer(self, renderer, quiet=False):
+    def _assign_renderer(self, renderer: 'py_cui.renderer.Renderer', quiet: bool=False) :
         """Function that assigns a renderer object to the element
 
         (Meant for internal usage only)
@@ -427,7 +416,7 @@ def _assign_renderer(self, renderer, quiet=False):
             raise py_cui.errors.PyCUIError('Invalid renderer, must be of type py_cui.renderer.Renderer')
 
 
-    def _contains_position(self, x, y):
+    def _contains_position(self, x: int, y: int) -> bool:
         """Checks if character position is within element.
 
         Parameters
@@ -486,7 +475,7 @@ class TextBoxImplementation(UIImplementation):
         Toggle to display password characters or text
     """
 
-    def __init__(self, initial_text, password, logger):
+    def __init__(self, initial_text: str, password: bool , logger):
         """Initializer for the TextBoxImplementation base class
         """
 
@@ -503,7 +492,7 @@ def __init__(self, initial_text, password, logger):
 
     # Variable getter + setter functions
 
-    def get_initial_cursor_pos(self):
+    def get_initial_cursor_pos(self) -> int:
         """Gets initial cursor position
 
         Returns
@@ -515,7 +504,7 @@ def get_initial_cursor_pos(self):
         return self._initial_cursor
 
 
-    def get_cursor_text_pos(self):
+    def get_cursor_text_pos(self) -> int:
         """Gets current position of cursor relative to text
 
         Returns
@@ -527,7 +516,7 @@ def get_cursor_text_pos(self):
         return self._cursor_text_pos
 
 
-    def get_cursor_limits(self):
+    def get_cursor_limits(self) -> Tuple[int,int]:
         """Gets cursor extreme points in terminal position
 
         Returns
@@ -539,7 +528,7 @@ def get_cursor_limits(self):
         return self._cursor_max_left, self._cursor_max_right
 
 
-    def get_cursor_position(self):
+    def get_cursor_position(self) -> Tuple[int,int]:
         """Returns current cursor poition
 
         Returns
@@ -551,7 +540,7 @@ def get_cursor_position(self):
         return self._cursor_x, self._cursor_y
 
 
-    def get_viewport_width(self):
+    def get_viewport_width(self) -> int:
         """Gets the width of the textbox viewport
 
         Returns
@@ -563,7 +552,7 @@ def get_viewport_width(self):
         return self._viewport_width
 
 
-    def set_text(self, text):
+    def set_text(self, text: str):
         """Sets the value of the text. Overwrites existing text
 
         Parameters
@@ -579,7 +568,7 @@ def set_text(self, text):
             self._cursor_x = self._cursor_x - diff
 
 
-    def get(self):
+    def get(self) -> str:
         """Gets value of the text in the textbox
 
         Returns
@@ -591,7 +580,7 @@ def get(self):
         return self._text
 
 
-    def clear(self):
+    def clear(self) -> None:
         """Clears the text in the textbox
         """
 
@@ -600,7 +589,7 @@ def clear(self):
         self._text             = ''
 
 
-    def _move_left(self):
+    def _move_left(self) -> None:
         """Shifts the cursor the the left. Internal use only
         """
 
@@ -610,7 +599,7 @@ def _move_left(self):
             self._cursor_text_pos = self._cursor_text_pos - 1
 
 
-    def _move_right(self):
+    def _move_right(self) -> None:
         """Shifts the cursor the the right. Internal use only
         """
         if self._cursor_text_pos < len(self._text):
@@ -619,7 +608,7 @@ def _move_right(self):
             self._cursor_text_pos = self._cursor_text_pos + 1
 
 
-    def _insert_char(self, key_pressed):
+    def _insert_char(self, key_pressed: int) -> None:
         """Inserts char at cursor position. Internal use only
 
         Parameters
@@ -633,7 +622,7 @@ def _insert_char(self, key_pressed):
         self._cursor_text_pos = self._cursor_text_pos + 1
 
 
-    def _jump_to_start(self):
+    def _jump_to_start(self) -> None:
         """Jumps to the start of the textbox. Internal use only
         """
 
@@ -641,7 +630,7 @@ def _jump_to_start(self):
         self._cursor_text_pos = 0
 
 
-    def _jump_to_end(self):
+    def _jump_to_end(self) -> None:
         """Jumps to the end to the textbox. Internal use only
         """
 
@@ -649,7 +638,7 @@ def _jump_to_end(self):
         self._cursor_x = self._initial_cursor + self._cursor_text_pos
 
 
-    def _erase_char(self):
+    def _erase_char(self) -> None:
         """Erases character at textbox cursor. Internal Use only
         """
 
@@ -660,7 +649,7 @@ def _erase_char(self):
             self._cursor_text_pos = self._cursor_text_pos - 1
 
 
-    def _delete_char(self):
+    def _delete_char(self) -> None:
         """Deletes character to right of texbox cursor. Internal use only
         """
 
@@ -693,9 +682,10 @@ def __init__(self, logger):
         self._selected_item    = 0
         self._page_scroll_len  = 5
         self._view_items       = []
+        self._on_selection_change: Optional[Callable[[Any],Any]] = None
 
 
-    def clear(self):
+    def clear(self) -> None:
         """Clears all items from the Scroll Menu
         """
 
@@ -706,8 +696,53 @@ def clear(self):
         self._logger.info('Clearing menu')
 
 
+    def set_on_selection_change_event(self, on_selection_change_event: Callable[[Any],Any]):
+        """Function that sets the function fired when menu selection changes.
 
-    def get_selected_item_index(self):
+        Event function must take 0 or 1 parameters. If 1 parameter, the new selcted item will be passed in.
+
+        Parameters
+        ----------
+        on_selection_change_event : Callable
+            Callable function that takes in as an argument the newly selected element
+
+        Raises
+        ------
+        TypeError
+            Raises a type error if event function is not callable
+        """
+
+        #mypy false-positive
+        if not isinstance(on_selection_change_event, Callable):  #type: ignore
+            raise TypeError('On selection change event must be a Callable!')
+        
+        self._on_selection_change = on_selection_change_event
+
+
+    def _process_selection_change_event(self):
+        """Function that executes on-selection change event either with the current menu item, or with no-args"""
+
+        # Identify num of args from callable. This allows for user to create commands that take in x, y
+        # coords of the mouse press as input
+        num_args = 0
+        try:
+            num_args = len(inspect.signature(self._on_selection_change).parameters)
+        except ValueError:
+            self._logger.error('Failed to get on_selection_change signature!')
+        except TypeError:
+            self._logger.error('Type of object not supported for signature identification!')
+
+        # Depending on the number of parameters for the self._on_selection_change, pass in the x and y
+        # values, or do nothing
+        if num_args == 1:
+            self._on_selection_change(self.get())
+        elif num_args == 0:
+            self._on_selection_change()
+        else:
+            raise ValueError('On selection change event must accept either 0 or 1 parameters!')
+
+
+    def get_selected_item_index(self) -> int:
         """Gets the currently selected item
 
         Returns
@@ -719,7 +754,7 @@ def get_selected_item_index(self):
         return self._selected_item
 
 
-    def set_selected_item_index(self, selected_item_index):
+    def set_selected_item_index(self, selected_item_index: int) -> None:
         """Sets the currently selected item
 
         Parameters
@@ -731,7 +766,7 @@ def set_selected_item_index(self, selected_item_index):
         self._selected_item = selected_item_index
 
 
-    def _scroll_up(self):
+    def _scroll_up(self) -> None:
         """Function that scrolls the view up in the scroll menu
         """
 
@@ -740,10 +775,10 @@ def _scroll_up(self):
         if self._selected_item > 0:
             self._selected_item = self._selected_item - 1
 
-        self._logger.info('Scrolling up to item {}'.format(self._selected_item))
+        self._logger.debug(f'Scrolling up to item {self._selected_item}')
 
 
-    def _scroll_down(self, viewport_height):
+    def _scroll_down(self, viewport_height: int) -> None:
         """Function that scrolls the view down in the scroll menu
 
         TODO: Viewport height should be calculated internally, and not rely on a parameter.
@@ -759,10 +794,10 @@ def _scroll_down(self, viewport_height):
         if self._selected_item > self._top_view + viewport_height:
             self._top_view = self._top_view + 1
 
-        self._logger.info('Scrolling down to item {}'.format(self._selected_item))
+        self._logger.debug(f'Scrolling down to item {self._selected_item}')
 
 
-    def _jump_up(self):
+    def _jump_up(self) -> None:
         """Function for jumping up menu several spots at a time
         """
 
@@ -770,7 +805,7 @@ def _jump_up(self):
             self._scroll_up()
 
 
-    def _jump_down(self, viewport_height):
+    def _jump_down(self, viewport_height: int) -> None:
         """Function for jumping down the menu several spots at a time
 
         Parameters
@@ -783,7 +818,7 @@ def _jump_down(self, viewport_height):
             self._scroll_down(viewport_height)
 
 
-    def _jump_to_top(self):
+    def _jump_to_top(self) -> None:
         """Function that jumps to the top of the menu
         """
 
@@ -791,7 +826,7 @@ def _jump_to_top(self):
         self._selected_item = 0
 
 
-    def _jump_to_bottom(self, viewport_height):
+    def _jump_to_bottom(self, viewport_height: int) -> None:
         """Function that jumps to the bottom of the menu
 
         Parameters
@@ -806,7 +841,7 @@ def _jump_to_bottom(self, viewport_height):
             self._top_view = 0
 
 
-    def add_item(self, item):
+    def add_item(self, item: Any) -> None: 
         """Adds an item to the menu.
 
         Parameters
@@ -815,11 +850,12 @@ def add_item(self, item):
             Object to add to the menu. Must have implemented __str__ function
         """
 
-        self._logger.info('Adding item {} to menu'.format(str(item)))
+        self._logger.debug(f'Adding item {str(item)} to menu')
         self._view_items.append(item)
 
 
-    def add_item_list(self, item_list):
+    def add_item_list(self, item_list: List[Any]) -> None:
+
         """Adds a list of items to the scroll menu.
 
         Parameters
@@ -828,24 +864,24 @@ def add_item_list(self, item_list):
             list of objects to add as items to the scrollmenu
         """
 
-        self._logger.info('Adding item list {} to menu'.format(str(item_list)))
+        self._logger.debug(f'Adding item list {str(item_list)} to menu')
         for item in item_list:
             self.add_item(item)
 
 
-    def remove_selected_item(self):
+    def remove_selected_item(self) -> None:
         """Function that removes the selected item from the scroll menu.
         """
 
         if len(self._view_items) == 0:
             return
-        self._logger.info('Removing {}'.format(str(self._view_items[self._selected_item])))
+        self._logger.debug(f'Removing {str(self._view_items[self._selected_item])}')
         del self._view_items[self._selected_item]
         if self._selected_item >= len(self._view_items) and self._selected_item > 0:
             self._selected_item = self._selected_item - 1
 
 
-    def remove_item(self, item):
+    def remove_item(self, item: Any) -> None:
         """Function that removes a specific item from the menu
 
         Parameters
@@ -856,14 +892,14 @@ def remove_item(self, item):
 
         if len(self._view_items) == 0 or item not in self._view_items:
             return
-        self._logger.info('Removing {}'.format(str(item)))
+        self._logger.debug(f'Removing {str(item)}')
         i_index = self._view_items.index(item)
         del self._view_items[i_index]
         if self._selected_item >= i_index:
             self._selected_item = self._selected_item - 1
 
 
-    def get_item_list(self):
+    def get_item_list(self) -> List[Any]:
         """Function that gets list of items in a scroll menu
 
         Returns
@@ -875,7 +911,7 @@ def get_item_list(self):
         return self._view_items
 
 
-    def get(self):
+    def get(self) -> Optional[Any]:
         """Function that gets the selected item from the scroll menu
 
         Returns
@@ -889,7 +925,7 @@ def get(self):
         return None
 
 
-    def set_selected_item(self, selected_item):
+    def set_selected_item(self, selected_item: Any):
         """Function that replaces the currently selected item with a new item
 
         Parameters
@@ -922,7 +958,7 @@ def __init__(self, logger, checked_char):
         self._checked_char       = checked_char
 
 
-    def add_item(self, item):
+    def add_item(self, item: Any) -> None:
         """Extends base class function, item is added and marked as unchecked to start
 
         Parameters
@@ -935,7 +971,7 @@ def add_item(self, item):
         self._selected_item_dict[item] = False
 
 
-    def remove_selected_item(self):
+    def remove_selected_item(self) -> None:
         """Removes selected item from item list and selected item dictionary
         """
 
@@ -943,7 +979,7 @@ def remove_selected_item(self):
         super().remove_selected_item()
 
 
-    def remove_item(self, item):
+    def remove_item(self, item) -> None:
         """Removes item from item list and selected item dict
 
         Parameters
@@ -956,18 +992,42 @@ def remove_item(self, item):
         super().remove_item(item)
 
 
-    def mark_item_as_checked(self, item):
+    def toggle_item_checked(self, item: Any):
         """Function that marks an item as selected
 
         Parameters
         ----------
         item : object
-            Mark item as checked
+            Toggle item checked state
         """
 
         self._selected_item_dict[item] = not self._selected_item_dict[item]
 
 
+    def mark_item_as_checked(self, item: Any) -> None:
+        """Function that marks an item as selected
+
+        Parameters
+        ----------
+        item : object
+            Toggle item checked state
+        """
+
+        self._selected_item_dict[item] = True
+
+
+    def mark_item_as_not_checked(self, item) -> None:
+        """Function that marks an item as selected
+
+        Parameters
+        ----------
+        item : object
+            Item to uncheck
+        """
+
+        self._selected_item_dict[item] = False
+
+
 class TextBlockImplementation(UIImplementation):
     """Base class for TextBlockImplementation
 
@@ -992,7 +1052,7 @@ class TextBlockImplementation(UIImplementation):
         The dimensions of the viewport in characters
     """
 
-    def __init__(self, initial_text, logger):
+    def __init__(self, initial_text: str, logger):
         """Initializer for TextBlockImplementation base class
 
         Zeros attributes, and parses initial text
@@ -1019,7 +1079,7 @@ def __init__(self, initial_text, logger):
 
     # Getters and setters
 
-    def get_viewport_start_pos(self):
+    def get_viewport_start_pos(self) -> Tuple[int,int]:
         """Gets upper left corner position of viewport
 
         Returns
@@ -1031,7 +1091,7 @@ def get_viewport_start_pos(self):
         return self._viewport_x_start, self._viewport_y_start
 
 
-    def get_viewport_dims(self):
+    def get_viewport_dims(self) -> Tuple[int,int]:
         """Gets viewport dimensions in characters
 
         Returns
@@ -1043,7 +1103,7 @@ def get_viewport_dims(self):
         return self._viewport_height, self._viewport_width
 
 
-    def get_cursor_text_pos(self):
+    def get_cursor_text_pos(self) -> Tuple[int,int]:
         """Gets cursor postion relative to text
 
         Returns
@@ -1056,7 +1116,7 @@ def get_cursor_text_pos(self):
         return self._cursor_text_pos_x, self._cursor_text_pos_y
 
 
-    def get_abs_cursor_position(self):
+    def get_abs_cursor_position(self) -> Tuple[int,int]:
         """Gets absolute cursor position in terminal characters
 
         Returns
@@ -1068,7 +1128,7 @@ def get_abs_cursor_position(self):
         return self._cursor_x, self._cursor_y
 
 
-    def get_cursor_limits_vertical(self):
+    def get_cursor_limits_vertical(self) -> Tuple[int,int]:
         """Gets limits for cursor in vertical direction
 
         Returns
@@ -1080,7 +1140,7 @@ def get_cursor_limits_vertical(self):
         return self._cursor_max_up, self._cursor_max_down
 
 
-    def get_cursor_limits_horizontal(self):
+    def get_cursor_limits_horizontal(self) -> Tuple[int,int]:
         """Gets limits for cursor in horizontal direction
 
         Returns
@@ -1092,7 +1152,7 @@ def get_cursor_limits_horizontal(self):
         return self._cursor_max_left, self._cursor_max_right
 
 
-    def get(self):
+    def get(self) -> str:
         """Gets all of the text in the textblock and returns it
 
         Returns
@@ -1103,11 +1163,11 @@ def get(self):
 
         text = ''
         for line in self._text_lines:
-            text = '{}{}\n'.format(text, line)
+            text = f'{text}{line}\n'
         return text
 
 
-    def write(self, text):
+    def write(self, text: str) -> None:
         """Function used for writing text to the text block
 
         Parameters
@@ -1123,7 +1183,7 @@ def write(self, text):
             self._text_lines.extend(lines)
 
 
-    def clear(self):
+    def clear(self) -> None:
         """Function that clears the text block
         """
 
@@ -1136,7 +1196,7 @@ def clear(self):
         self._logger.info('Cleared textblock')
 
 
-    def get_current_line(self):
+    def get_current_line(self) -> str:
         """Returns the line on which the cursor currently resides
 
         Returns
@@ -1148,7 +1208,7 @@ def get_current_line(self):
         return self._text_lines[self._cursor_text_pos_y]
 
 
-    def set_text(self, text):
+    def set_text(self, text: str) -> None:
         """Function that sets the text for the textblock.
 
         Note that this will overwrite any existing text
@@ -1170,7 +1230,7 @@ def set_text(self, text):
         self._cursor_text_pos_x    = 0
 
 
-    def set_text_line(self, text):
+    def set_text_line(self, text: str) -> None:
         """Function that sets the current line's text.
 
         Meant only for internal use
@@ -1184,7 +1244,7 @@ def set_text_line(self, text):
         self._text_lines[self._cursor_text_pos_y] = text
 
 
-    def _move_left(self):
+    def _move_left(self) -> None:
         """Function that moves the cursor/text position one location to the left
         """
 
@@ -1195,10 +1255,10 @@ def _move_left(self):
                 self._viewport_x_start = self._viewport_x_start - 1
             self._cursor_text_pos_x = self._cursor_text_pos_x - 1
 
-        self._logger.info('Moved cursor left to pos {}'.format(self._cursor_text_pos_x))
+        self._logger.debug(f'Moved cursor left to pos {self._cursor_text_pos_x}')
 
 
-    def _move_right(self):
+    def _move_right(self) -> None:
         """Function that moves the cursor/text position one location to the right
         """
 
@@ -1211,10 +1271,10 @@ def _move_right(self):
                 self._viewport_x_start = self._viewport_x_start + 1
             self._cursor_text_pos_x = self._cursor_text_pos_x + 1
 
-        self._logger.info('Moved cursor right to pos {}'.format(self._cursor_text_pos_x))
+        self._logger.debug(f'Moved cursor right to pos {self._cursor_text_pos_x}')
 
 
-    def _move_up(self):
+    def _move_up(self) -> None:
         """Function that moves the cursor/text position one location up
         """
 
@@ -1230,10 +1290,10 @@ def _move_up(self):
                 self._cursor_x = self._cursor_x - (self._cursor_text_pos_x - temp)
                 self._cursor_text_pos_x = temp
 
-        self._logger.info('Moved cursor up to line {}'.format(self._cursor_text_pos_y))
+        self._logger.debug(f'Moved cursor up to line {self._cursor_text_pos_y}')
 
 
-    def _move_down(self):
+    def _move_down(self) -> None:
         """Function that moves the cursor/text position one location down
         """
 
@@ -1248,16 +1308,16 @@ def _move_down(self):
                 self._cursor_x = self._cursor_x - (self._cursor_text_pos_x - temp)
                 self._cursor_text_pos_x = temp
 
-        self._logger.info('Moved cursor down to line {}'.format(self._cursor_text_pos_y))
+        self._logger.debug(f'Moved cursor down to line {self._cursor_text_pos_y}')
 
 
 
-    def _handle_newline(self):
+    def _handle_newline(self) -> None:
         """Function that handles recieving newline characters in the text
         """
 
         current_line = self.get_current_line()
-        self._logger.info('Inserting newline in location {}'.format(self._cursor_text_pos_x))
+        self._logger.debug(f'Inserting newline in location {self._cursor_text_pos_x}')
 
         new_line_1 = current_line[:self._cursor_text_pos_x]
         new_line_2 = current_line[self._cursor_text_pos_x:]
@@ -1273,12 +1333,12 @@ def _handle_newline(self):
             self._viewport_y_start = self._viewport_y_start + 1
 
 
-    def _handle_backspace(self):
+    def _handle_backspace(self) -> None:
         """Function that handles recieving backspace characters in the text
         """
 
         current_line = self.get_current_line()
-        self._logger.info('Inserting backspace in location {}'.format(self._cursor_text_pos_x))
+        self._logger.debug(f'Inserting backspace in location {self._cursor_text_pos_x}')
 
         if self._cursor_text_pos_x == 0 and self._cursor_text_pos_y != 0:
             self._cursor_text_pos_x = len(self._text_lines[self._cursor_text_pos_y - 1])
@@ -1297,23 +1357,23 @@ def _handle_backspace(self):
             self._cursor_text_pos_x = self._cursor_text_pos_x - 1
 
 
-    def _handle_home(self):
+    def _handle_home(self) -> None:
         """Function that handles recieving a home keypress
         """
 
-        self._logger.info('Inserting Home')
+        self._logger.debug('Inserting Home')
 
         self._cursor_x = self._cursor_max_left
         self._cursor_text_pos_x = 0
         self._viewport_x_start = 0
 
 
-    def _handle_end(self):
+    def _handle_end(self) -> None:
         """Function that handles recieving an end keypress
         """
 
         current_line = self.get_current_line()
-        self._logger.info('Inserting End')
+        self._logger.debug('Inserting End')
 
         self._cursor_text_pos_x = len(current_line)
         if len(current_line) > self._viewport_width:
@@ -1323,12 +1383,12 @@ def _handle_end(self):
             self._cursor_x = self._cursor_max_left + len(current_line)
 
 
-    def _handle_delete(self):
+    def _handle_delete(self) -> None:
         """Function that handles recieving a delete keypress
         """
 
         current_line = self.get_current_line()
-        self._logger.info('Inserting delete to pos {}'.format(self._cursor_text_pos_x))
+        self._logger.debug(f'Inserting delete to pos {self._cursor_text_pos_x}')
 
         if self._cursor_text_pos_x == len(current_line) and self._cursor_text_pos_y < len(self._text_lines) - 1:
             self._text_lines[self._cursor_text_pos_y] = self._text_lines[self._cursor_text_pos_y] + self._text_lines[self._cursor_text_pos_y + 1]
@@ -1337,7 +1397,7 @@ def _handle_delete(self):
             self.set_text_line(current_line[:self._cursor_text_pos_x] + current_line[self._cursor_text_pos_x+1:])
 
 
-    def _insert_char(self, key_pressed):
+    def _insert_char(self, key_pressed: int) -> None:
         """Function that handles recieving a character
 
         Parameters
@@ -1347,7 +1407,7 @@ def _insert_char(self, key_pressed):
         """
 
         current_line = self.get_current_line()
-        self._logger.info('Inserting character {} to pos {}'.format(chr(key_pressed), self._cursor_text_pos_x))
+        self._logger.debug(f'Inserting character {chr(key_pressed)} to pos {self._cursor_text_pos_x}')
 
         self.set_text_line(current_line[:self._cursor_text_pos_x] + chr(key_pressed) + current_line[self._cursor_text_pos_x:])
         if len(current_line) <= self._viewport_width:
diff --git a/py_cui/widget_set.py b/py_cui/widget_set.py
index 846a6c5..94e6791 100644
--- a/py_cui/widget_set.py
+++ b/py_cui/widget_set.py
@@ -9,10 +9,14 @@
 # TODO: Should create an initial widget set in PyCUI class that widgets are added to by default.
 
 import shutil
+from typing import Any, Callable, Dict, List, Optional, TYPE_CHECKING
 import py_cui.widgets as widgets
 import py_cui.grid as grid
 import py_cui.controls as controls
 
+if TYPE_CHECKING:
+    import py_cui
+
 
 class WidgetSet:
     """Class that represents a collection of widgets.
@@ -29,15 +33,18 @@ class WidgetSet:
         list of keybindings to check against in the main CUI loop
     height, width : int
         height of the terminal in characters, width of terminal in characters
+    root : py_cui.PyCUI
+        Main PyCUI object reference
     """
 
-    def __init__(self, num_rows, num_cols, logger, simulated_terminal=None):
+    def __init__(self, num_rows: int, num_cols: int, logger: 'py_cui.debug.PyCUILogger', root:'py_cui.PyCUI', simulated_terminal: Optional[List[int]] =None):
         """Constructor for WidgetSet
         """
 
-        self._widgets      = {}
-        self._keybindings  = {}
+        self._widgets: Dict[int,Optional['py_cui.widgets.Widget']]      = {}
+        self._keybindings: Dict[int,Callable[[],Any]]  = {}
 
+        self._root = root
         self._simulated_terminal = simulated_terminal
 
         if self._simulated_terminal is None:
@@ -50,15 +57,16 @@ def __init__(self, num_rows, num_cols, logger, simulated_terminal=None):
 
         self._height = height
         self._width = width
-        self._height = self._height - 4
+        status_bars_height = self._root.title_bar.get_height() + self._root.status_bar.get_height()
+        self._height = self._height - status_bars_height - 2
 
         self._grid = grid.Grid(num_rows, num_cols, self._height, self._width, logger)
 
-        self._selected_widget = None
+        self._selected_widget: Optional[int] = None
         self._logger = logger
 
 
-    def set_selected_widget(self, widget_id):
+    def set_selected_widget(self, widget_id: int) -> None:
         """Function that sets the selected cell for the CUI
 
         Parameters
@@ -71,7 +79,7 @@ def set_selected_widget(self, widget_id):
             self._selected_widget = widget_id
 
 
-    def get_widgets(self):
+    def get_widgets(self) -> Dict[int, Optional['py_cui.widgets.Widget']]:
         """Function that gets current set of widgets
 
         Returns
@@ -83,7 +91,7 @@ def get_widgets(self):
         return self._widgets
 
 
-    def add_key_command(self, key, command):
+    def add_key_command(self, key: int, command: Callable[[],Any]):
         """Function that adds a keybinding to the CUI when in overview mode
 
         Parameters
@@ -97,7 +105,7 @@ def add_key_command(self, key, command):
         self._keybindings[key] = command
 
 
-    def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0):
+    def add_scroll_menu(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.ScrollMenu':
         """Function that adds a new scroll menu to the CUI grid
 
         Parameters
@@ -123,7 +131,7 @@ def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, pad
             A reference to the created scroll menu object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_scroll_menu     = widgets.ScrollMenu(id,
                                                  title,
                                                  self._grid,
@@ -137,11 +145,11 @@ def add_scroll_menu(self, title, row, column, row_span = 1, column_span = 1, pad
         self._widgets[id]  = new_scroll_menu
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_scroll_menu))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_scroll_menu))}')
         return new_scroll_menu
 
 
-    def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=1, pady=0, checked_char='X'):
+    def add_checkbox_menu(self, title: str, row: int, column: int, row_span: int=1, column_span: int=1, padx: int=1, pady: int=0, checked_char: str='X') -> 'py_cui.widgets.CheckBoxMenu':
         """Function that adds a new checkbox menu to the CUI grid
 
         Parameters
@@ -169,7 +177,7 @@ def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=
             A reference to the created checkbox object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_checkbox_menu   = widgets.CheckBoxMenu(id,
                                                    title,
                                                    self._grid,
@@ -184,11 +192,11 @@ def add_checkbox_menu(self, title, row, column, row_span=1, column_span=1, padx=
         self._widgets[id]  = new_checkbox_menu
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_checkbox_menu))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_checkbox_menu))}')
         return new_checkbox_menu
 
 
-    def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = '', password = False):
+    def add_text_box(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '', password: bool = False) -> 'py_cui.widgets.TextBox':
         """Function that adds a new text box to the CUI grid
 
         Parameters
@@ -218,7 +226,7 @@ def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx =
             A reference to the created textbox object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_text_box        = widgets.TextBox(id,
                                               title,
                                               self._grid,
@@ -232,11 +240,11 @@ def add_text_box(self, title, row, column, row_span = 1, column_span = 1, padx =
         self._widgets[id]    = new_text_box
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_text_box))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_text_box))}')
         return new_text_box
 
 
-    def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, initial_text = ''):
+    def add_text_block(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, initial_text: str = '') -> 'py_cui.widgets.ScrollTextBlock':
         """Function that adds a new text block to the CUI grid
 
         Parameters
@@ -264,7 +272,7 @@ def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx
             A reference to the created textblock object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_text_block      = widgets.ScrollTextBlock(id,
                                                       title,
                                                       self._grid,
@@ -279,11 +287,11 @@ def add_text_block(self, title, row, column, row_span = 1, column_span = 1, padx
         self._widgets[id]  = new_text_block
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_text_block))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_text_block))}')
         return new_text_block
 
 
-    def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0):
+    def add_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0) -> 'py_cui.widgets.Label':
         """Function that adds a new label to the CUI grid
 
         Parameters
@@ -309,7 +317,7 @@ def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1,
             A reference to the created label object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_label           = widgets.Label(id,
                                             title,
                                             self._grid,
@@ -321,11 +329,11 @@ def add_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1,
                                             pady,
                                             self._logger)
         self._widgets[id]  = new_label
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_label))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_label))}')
         return new_label
 
 
-    def add_block_label(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, center=True):
+    def add_block_label(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, center: bool=True) -> 'py_cui.widgets.BlockLabel':
         """Function that adds a new block label to the CUI grid
 
         Parameters
@@ -353,7 +361,7 @@ def add_block_label(self, title, row, column, row_span = 1, column_span = 1, pad
             A reference to the created block label object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_label           = widgets.BlockLabel(id,
                                                  title,
                                                  self._grid,
@@ -366,11 +374,11 @@ def add_block_label(self, title, row, column, row_span = 1, column_span = 1, pad
                                                  center,
                                                  self._logger)
         self._widgets[id]  = new_label
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_label))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_label))}')
         return new_label
 
 
-    def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1, pady = 0, command=None):
+    def add_button(self, title: str, row: int, column: int, row_span: int = 1, column_span: int = 1, padx: int = 1, pady: int = 0, command: Optional[Callable[[],Any]]=None) -> 'py_cui.widgets.Button':
         """Function that adds a new button to the CUI grid
 
         Parameters
@@ -398,7 +406,7 @@ def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1
             A reference to the created button object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self.get_widgets().keys())
         new_button          = widgets.Button(id,
                                              title,
                                              self._grid,
@@ -413,13 +421,15 @@ def add_button(self, title, row, column, row_span = 1, column_span = 1, padx = 1
         self._widgets[id]  = new_button
         if self._selected_widget is None:
             self.set_selected_widget(id)
-        self._logger.info('Adding widget {} w/ ID {} of type {}'.format(title, id, str(type(new_button))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_button))}')
         return new_button
 
 
-    def add_slider(self, title, row, column, row_span=1,
-                   column_span=1, padx=1, pady=0,
-                   min_val=0, max_val=100, step=1, init_val=0):
+    def add_slider(self, title: str, row: int, column: int, row_span: int=1,
+                   column_span: int=1, padx: int=1, pady: int=0,
+                   min_val: int=0, max_val: int=100, step: int=1, init_val: int=0) -> 'py_cui.controls.slider.SliderWidget':     
+                   
+                   
         """Function that adds a new label to the CUI grid
 
         Parameters
@@ -454,7 +464,7 @@ def add_slider(self, title, row, column, row_span=1,
             A reference to the created slider object.
         """
 
-        id = 'Widget{}'.format(len(self._widgets.keys()))
+        id = len(self._widgets.keys())
         new_slider = controls.slider.SliderWidget(id,
                                                   title,
                                                   self._grid,
@@ -470,6 +480,5 @@ def add_slider(self, title, row, column, row_span=1,
                                                   step,
                                                   init_val)
         self._widgets[id] = new_slider
-        self._logger.info('Adding widget {} w/ ID {} of type {}'
-                           .format(title, id, str(type(new_slider))))
+        self._logger.debug(f'Adding widget {title} w/ ID {id} of type {str(type(new_slider))}')
         return new_slider
diff --git a/py_cui/widgets.py b/py_cui/widgets.py
index c9b3aa3..f119527 100644
--- a/py_cui/widgets.py
+++ b/py_cui/widgets.py
@@ -1,4 +1,4 @@
-"""Module contatining all core widget classes for py_cui.
+"""Module containing all core widget classes for py_cui.
 
 Widgets are the basic building blocks of a user interface made with py_cui.
 This module contains classes for:
@@ -22,11 +22,14 @@
 
 
 import curses
+import inspect
 import py_cui
 import py_cui.ui
 import py_cui.colors
 import py_cui.errors
 
+from typing import Callable, List, Dict, Tuple, Any, Optional
+
 
 class Widget(py_cui.ui.UIElement):
     """Top Level Widget Base Class
@@ -50,10 +53,10 @@ class Widget(py_cui.ui.UIElement):
         color rules to load into renderer when drawing widget
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, selectable = True):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, selectable: bool = True):
         """Initializer for base widget class
 
-        Calss UIElement superclass initialzier, and then assigns widget to grid, along with row/column info
+        Class UIElement superclass initializer, and then assigns widget to grid, along with row/column info
         and color rules and key commands
         """
 
@@ -64,7 +67,7 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self._grid = grid
         grid_rows, grid_cols = self._grid.get_dimensions()
         if (grid_cols < column + column_span) or (grid_rows < row + row_span):
-            raise py_cui.errors.PyCUIOutOfBoundsError("Target grid too small for widget {}".format(title))
+            raise py_cui.errors.PyCUIOutOfBoundsError(f"Target grid too small for widget {title}")
 
         self._row          = row
         self._column       = column
@@ -73,14 +76,15 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self._padx         = padx
         self._pady         = pady
         self._selectable       = selectable
-        self._key_commands     = {}
-        self._text_color_rules = []
+        self._key_commands: Dict[int,Callable[[],Any]]     = {}
+        self._mouse_commands: Dict[int,Callable[[],Any]]   = {}
+        self._text_color_rules: List['py_cui.ColorRule'] = []
         self._default_color = py_cui.WHITE_ON_BLACK
         self._border_color = self._default_color
         self.update_height_width()
 
 
-    def add_key_command(self, key, command):
+    def add_key_command(self, key: int, command: Callable[[],Any]) -> Any:
         """Maps a keycode to a function that will be executed when in focus mode
 
         Parameters
@@ -94,7 +98,32 @@ def add_key_command(self, key, command):
         self._key_commands[key] = command
 
 
-    def update_key_command(self, key, command):
+    def add_mouse_command(self, mouse_event: int, command: Callable[[],Any]) -> None:
+        """Maps a keycode to a function that will be executed when in focus mode
+
+        Parameters
+        ----------
+        key : py_cui.keys.MOUSE_EVENT
+            Mouse event code from py_cui.keys
+        command : Callable
+            a non-argument function or lambda function to execute if in focus mode and key is pressed
+
+        Raises
+        ------
+        PyCUIError
+            If input mouse event code is not valid
+        """
+
+        if mouse_event not in py_cui.keys.MOUSE_EVENTS:
+            raise py_cui.errors.PyCUIError(f'Event code {mouse_event} is not a valid py_cui mouse event!')
+
+        if mouse_event in self._mouse_commands.keys():
+            self._logger.warn(f'Overriding mouse command for event {mouse_event}')
+
+        self._mouse_commands[mouse_event] = command
+
+
+    def update_key_command(self, key: int, command: Callable[[],Any]) -> Any:
         """Maps a keycode to a function that will be executed when in focus mode, if key is already mapped
 
         Parameters
@@ -109,7 +138,7 @@ def update_key_command(self, key, command):
             self.add_key_command(key, command)
 
 
-    def add_text_color_rule(self, regex, color, rule_type, match_type='line', region=[0,1], include_whitespace=False, selected_color=None):
+    def add_text_color_rule(self, regex: str, color: int, rule_type: str, match_type: str='line', region: List[int]=[0,1], include_whitespace: bool=False, selected_color=None) -> None:
         """Forces renderer to draw text using given color if text_condition_function returns True
 
         Parameters
@@ -136,7 +165,7 @@ def add_text_color_rule(self, regex, color, rule_type, match_type='line', region
         self._text_color_rules.append(new_color_rule)
 
 
-    def get_absolute_start_pos(self):
+    def get_absolute_start_pos(self) -> Tuple[int,int]:
         """Gets the absolute position of the widget in characters. Override of base class function
 
         Returns
@@ -156,12 +185,11 @@ def get_absolute_start_pos(self):
             y_adjust = offset_y
 
         x_pos = self._column * col_width + x_adjust
-        # Always add two to the y_pos, because we have a title bar + a pad row
-        y_pos = self._row * row_height + 2 + y_adjust
+        y_pos = self._row * row_height + y_adjust + self._grid._title_bar_offset + 1
         return x_pos, y_pos
 
 
-    def get_absolute_stop_pos(self):
+    def get_absolute_stop_pos(self) -> Tuple[int,int]:
         """Gets the absolute dimensions of the widget in characters. Override of base class function
 
         Returns
@@ -189,7 +217,7 @@ def get_absolute_stop_pos(self):
         return width + self._start_x, height + self._start_y
 
 
-    def get_grid_cell(self):
+    def get_grid_cell(self) -> Tuple[int,int]:
         """Gets widget row, column in grid
 
         Returns
@@ -201,7 +229,7 @@ def get_grid_cell(self):
         return self._row, self._column
 
 
-    def get_grid_cell_spans(self):
+    def get_grid_cell_spans(self) -> Tuple[int,int]:
         """Gets widget row span, column span in grid
 
         Returns
@@ -213,7 +241,7 @@ def get_grid_cell_spans(self):
         return self._row_span, self._column_span
 
 
-    def set_selectable(self, selectable):
+    def set_selectable(self, selectable: bool) -> None:
         """Setter for widget selectablility
 
         Paramters
@@ -225,7 +253,7 @@ def set_selectable(self, selectable):
         self._selectable = selectable
 
 
-    def is_selectable(self):
+    def is_selectable(self) -> bool:
         """Checks if the widget is selectable
 
         Returns
@@ -237,7 +265,7 @@ def is_selectable(self):
         return self._selectable
 
 
-    def _is_row_col_inside(self, row, col):
+    def _is_row_col_inside(self, row: int, col: int) -> bool:
         """Checks if a particular row + column is inside the widget area
 
         Parameters
@@ -263,7 +291,41 @@ def _is_row_col_inside(self, row, col):
     # BELOW FUNCTIONS SHOULD BE OVERWRITTEN BY SUB-CLASSES
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int):
+        """Base class function that handles all assigned mouse presses.
+
+        When overwriting this function, make sure to add a super()._handle_mouse_press(x, y, mouse_event) call,
+        as this is required for user defined key command support
+
+        Parameters
+        ----------
+        key_pressed : int
+            key code of key pressed
+        """
+
+        # Retrieve the command function if it exists
+        if mouse_event in self._mouse_commands.keys():
+            command = self._mouse_commands[mouse_event]
+            
+            # Identify num of args from callable. This allows for user to create commands that take in x, y
+            # coords of the mouse press as input
+            num_args = 0
+            try:
+                num_args = len(inspect.signature(command).parameters)
+            except ValueError:
+                self._logger.error('Failed to get mouse press command signature!')
+            except TypeError:
+                self._logger.error('Type of object not supported for signature identification!')
+
+            # Depending on the number of parameters for the command, pass in the x and y
+            # values, or do nothing
+            if num_args == 2:
+                command(x, y)
+            else:
+                command()
+
+
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Base class function that handles all assigned key presses.
 
         When overwriting this function, make sure to add a super()._handle_key_press(key_pressed) call,
@@ -280,7 +342,7 @@ def _handle_key_press(self, key_pressed):
             command()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Base class draw class that checks if renderer is valid.
 
         Should be called with super()._draw() in overrides.
@@ -304,7 +366,7 @@ class Label(Widget):
         Toggle for drawing label border
     """
 
-    def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, pady, logger):
+    def __init__(self, id, title: str,  grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger):
         """Initalizer for Label widget
         """
 
@@ -312,14 +374,14 @@ def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, p
         self._draw_border = False
 
 
-    def toggle_border(self):
+    def toggle_border(self) -> None:
         """Function that gives option to draw border around label
         """
 
         self._draw_border = not self._draw_border
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override base draw class.
 
         Center text and draw it
@@ -345,7 +407,7 @@ class BlockLabel(Widget):
         Decides whether or not label should be centered
     """
 
-    def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, pady, center, logger):
+    def __init__(self, id, title: str,  grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, center, logger):
         """Initializer for blocklabel widget
         """
 
@@ -355,7 +417,7 @@ def __init__(self, id, title,  grid, row, column, row_span, column_span, padx, p
         self._draw_border  = False
 
 
-    def set_title(self, title):
+    def set_title(self, title: str) -> None:
         """Override of base class, splits title into lines for rendering line by line.
 
         Parameters
@@ -368,14 +430,14 @@ def set_title(self, title):
         self._lines = title.splitlines()
 
 
-    def toggle_border(self):
+    def toggle_border(self) -> None:
         """Function that gives option to draw border around label
         """
 
         self._draw_border = not self._draw_border
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override base draw class.
 
         Center text and draw it
@@ -398,7 +460,7 @@ class ScrollMenu(Widget, py_cui.ui.MenuImplementation):
     """A scroll menu widget.
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger: 'py_cui.debug.PyCUILogger'):
         """Initializer for scroll menu. calls superclass initializers and sets help text
         """
 
@@ -407,7 +469,7 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self.set_help_text('Focus mode on ScrollMenu. Use Up/Down/PgUp/PgDown/Home/End to scroll, Esc to exit.')
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int):
         """Override of base class function, handles mouse press in menu
 
         Parameters
@@ -416,14 +478,25 @@ def _handle_mouse_press(self, x, y):
             Coordinates of mouse press
         """
 
-        super()._handle_mouse_press(x, y)
-        viewport_top = self._start_y + self._pady + 1
-        if viewport_top <= y and viewport_top + len(self._view_items) - self._top_view >= y:
-            elem_clicked = y - viewport_top + self._top_view
-            self.set_selected_item_index(elem_clicked)
+        # For either click or double click we want to jump to the clicked-on item
+        if mouse_event == py_cui.keys.LEFT_MOUSE_CLICK or mouse_event == py_cui.keys.LEFT_MOUSE_DBL_CLICK:
+            current = self.get_selected_item_index()
+            viewport_top = self._start_y + self._pady + 1
+
+            if viewport_top <= y and viewport_top + len(self._view_items) - self._top_view >= y:
+                elem_clicked = y - viewport_top + self._top_view
+                self.set_selected_item_index(elem_clicked)
+        
+            if self.get_selected_item_index() != current and self._on_selection_change is not None:
+                self._process_selection_change_event()
 
+        # For scroll menu, handle custom mouse press after initial event, since we will likely want to
+        # have access to the newly selected item
+        Widget._handle_mouse_press(self, x, y, mouse_event)
 
-    def _handle_key_press(self, key_pressed):
+
+
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override base class function.
 
         UP_ARROW scrolls up, DOWN_ARROW scrolls down.
@@ -434,8 +507,11 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        Widget._handle_key_press(self, key_pressed)
+
+        current = self.get_selected_item_index()
         viewport_height = self.get_viewport_height()
+        
         if key_pressed == py_cui.keys.KEY_UP_ARROW:
             self._scroll_up()
         if key_pressed == py_cui.keys.KEY_DOWN_ARROW:
@@ -448,13 +524,16 @@ def _handle_key_press(self, key_pressed):
             self._jump_up()
         if key_pressed == py_cui.keys.KEY_PAGE_DOWN:
             self._jump_down(viewport_height)
+        if self.get_selected_item_index() != current and self._on_selection_change is not None:
+
+            self._process_selection_change_event()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Overrides base class draw function
         """
 
-        super()._draw()
+        Widget._draw(self)
         self._renderer.set_color_mode(self._color)
         self._renderer.draw_border(self)
         counter = self._pady + 1
@@ -487,7 +566,7 @@ class CheckBoxMenu(Widget, py_cui.ui.CheckBoxMenuImplementation):
         Character to represent a checked item
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, checked_char):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, checked_char: str):
         """Initializer for CheckBoxMenu Widget
         """
 
@@ -496,7 +575,7 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self.set_help_text('Focus mode on CheckBoxMenu. Use up/down to scroll, Enter to toggle set, unset, Esc to exit.')
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Override of base class function, handles mouse press in menu
 
         Parameters
@@ -505,7 +584,7 @@ def _handle_mouse_press(self, x, y):
             Coordinates of mouse press
         """
 
-        super()._handle_mouse_press(x, y)
+        Widget._handle_mouse_press(self, x, y, mouse_event)
         viewport_top = self._start_y + self._pady + 1
         if viewport_top <= y and viewport_top + len(self._view_items) - self._top_view >= y:
             elem_clicked = y - viewport_top + self._top_view
@@ -513,7 +592,7 @@ def _handle_mouse_press(self, x, y):
             self.mark_item_as_checked(self._view_items[elem_clicked])
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of key presses.
 
         First, run the superclass function, scrolling should still work.
@@ -540,10 +619,10 @@ def _handle_key_press(self, key_pressed):
         if key_pressed == py_cui.keys.KEY_PAGE_DOWN:
             self._jump_down(viewport_height)
         if key_pressed == py_cui.keys.KEY_ENTER:
-            self.mark_item_as_checked(self.get())
+            self.toggle_item_checked(self.get())
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Overrides base class draw function
         """
 
@@ -554,9 +633,9 @@ def _draw(self):
         line_counter = 0
         for item in self._view_items:
             if self._selected_item_dict[item]:
-                line = '[{}] - {}'.format(self._checked_char, str(item))
+                line = f'[{self._checked_char}] - {str(item)}'
             else:
-                line = '[ ] - {}'.format(str(item))
+                line = f'[ ] - {str(item)}'
             if line_counter < self._top_view:
                 line_counter = line_counter + 1
             else:
@@ -584,7 +663,7 @@ class Button(Widget):
         A no-args function to run when the button is pressed.
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, command):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, command: Optional[Callable[[],Any]]):
         """Initializer for Button Widget
         """
 
@@ -592,9 +671,14 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self.command = command
         self.set_color(py_cui.MAGENTA_ON_BLACK)
         self.set_help_text('Focus mode on Button. Press Enter to press button, Esc to exit focus mode.')
+        
+        # By default we will process command on click or double click
+        if self.command is not None:
+            self.add_mouse_command(py_cui.keys.LEFT_MOUSE_CLICK, self.command)
+            self.add_mouse_command(py_cui.keys.LEFT_MOUSE_DBL_CLICK, self.command)
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class, adds ENTER listener that runs the button's command
 
         Parameters
@@ -609,7 +693,7 @@ def _handle_key_press(self, key_pressed):
                 return self.command()
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class draw function
         """
 
@@ -627,7 +711,7 @@ class TextBox(Widget, py_cui.ui.TextBoxImplementation):
     """Widget for entering small single lines of text
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, initial_text, password):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, initial_text: str, password: bool):
         """Initializer for TextBox widget. Uses TextBoxImplementation as base
         """
 
@@ -637,11 +721,11 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self.set_help_text('Focus mode on TextBox. Press Esc to exit focus mode.')
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Need to update all cursor positions on resize
         """
 
-        super().update_height_width()
+        Widget.update_height_width(self)
         padx, _             = self.get_padding()
         start_x, start_y    = self.get_start_position()
         height, width       = self.get_absolute_dimensions()
@@ -654,7 +738,7 @@ def update_height_width(self):
         self._viewport_width     = self._cursor_max_right - self._cursor_max_left
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Override of base class function, handles mouse press in menu
 
         Parameters
@@ -663,7 +747,7 @@ def _handle_mouse_press(self, x, y):
             Coordinates of mouse press
         """
 
-        super()._handle_mouse_press(x, y)
+        Widget._handle_mouse_press(self, x, y, mouse_event)
         if y == self._cursor_y and x >= self._cursor_max_left and x <= self._cursor_max_right:
             if x <= len(self._text) + self._cursor_max_left:
                 old_text_pos = self._cursor_text_pos
@@ -675,7 +759,7 @@ def _handle_mouse_press(self, x, y):
                 self._cursor_text_pos = len(self._text)
 
 
-    def _handle_key_press(self, key_pressed):
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base handle key press function
 
         Parameters
@@ -684,7 +768,7 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        Widget._handle_key_press(self, key_pressed)
         if key_pressed == py_cui.keys.KEY_LEFT_ARROW:
             self._move_left()
         elif key_pressed == py_cui.keys.KEY_RIGHT_ARROW:
@@ -702,11 +786,11 @@ def _handle_key_press(self, key_pressed):
             self._insert_char(key_pressed)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base draw function
         """
 
-        super()._draw()
+        Widget._draw(self)
 
         self._renderer.set_color_mode(self._color)
         self._renderer.draw_text(self, self._title, self._cursor_y - 2, bordered=False)
@@ -734,7 +818,7 @@ class ScrollTextBlock(Widget, py_cui.ui.TextBlockImplementation):
     """Widget for editing large multi-line blocks of text
     """
 
-    def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pady, logger, initial_text):
+    def __init__(self, id, title: str, grid: 'py_cui.grid.Grid', row: int, column: int, row_span: int, column_span: int, padx: int, pady: int, logger, initial_text: str):
         """Initializer for TextBlock Widget. Uses TextBlockImplementation as base
         """
 
@@ -744,7 +828,7 @@ def __init__(self, id, title, grid, row, column, row_span, column_span, padx, pa
         self.set_help_text('Focus mode on TextBlock. Press Esc to exit focus mode.')
 
 
-    def update_height_width(self):
+    def update_height_width(self) -> None:
         """Function that updates the position of the text and cursor on resize
         """
 
@@ -763,7 +847,7 @@ def update_height_width(self):
         self._viewport_height    = self._cursor_max_down  - self._cursor_max_up
 
 
-    def _handle_mouse_press(self, x, y):
+    def _handle_mouse_press(self, x: int, y: int, mouse_event: int) -> None:
         """Override of base class function, handles mouse press in menu
 
         Parameters
@@ -772,30 +856,32 @@ def _handle_mouse_press(self, x, y):
             Coordinates of mouse press
         """
 
-        super()._handle_mouse_press(x, y)
-        if y >= self._cursor_max_up and y <= self._cursor_max_down:
-            if x >= self._cursor_max_left and x <= self._cursor_max_right:
-                line_clicked_index = y - self._cursor_max_up + self._viewport_y_start
-                if len(self._text_lines) <= line_clicked_index:
-                    self._cursor_text_pos_y = len(self._text_lines) - 1
-                    self._cursor_y = self._cursor_max_up + self._cursor_text_pos_y - self._viewport_y_start
-                    line = self._text_lines[len(self._text_lines) - 1]
-                else:
-                    self._cursor_text_pos_y = line_clicked_index
-                    self._cursor_y = y
-                    line = self._text_lines[line_clicked_index]
-                
-                if x <= len(line) + self._cursor_max_left:
-                    old_text_pos = self._cursor_text_pos_x
-                    old_cursor_x = self._cursor_x
-                    self._cursor_x = x
-                    self._cursor_text_pos_x = old_text_pos + (x - old_cursor_x)
-                else:
-                    self._cursor_x = self._cursor_max_left + len(line)
-                    self._cursor_text_pos_x = len(line)
-
-
-    def _handle_key_press(self, key_pressed):
+        Widget._handle_mouse_press(self, x, y, mouse_event)
+        
+        if mouse_event == py_cui.keys.LEFT_MOUSE_CLICK:
+            if y >= self._cursor_max_up and y <= self._cursor_max_down:
+                if x >= self._cursor_max_left and x <= self._cursor_max_right:
+                    line_clicked_index = y - self._cursor_max_up + self._viewport_y_start
+                    if len(self._text_lines) <= line_clicked_index:
+                        self._cursor_text_pos_y = len(self._text_lines) - 1
+                        self._cursor_y = self._cursor_max_up + self._cursor_text_pos_y - self._viewport_y_start
+                        line = self._text_lines[len(self._text_lines) - 1]
+                    else:
+                        self._cursor_text_pos_y = line_clicked_index
+                        self._cursor_y = y
+                        line = self._text_lines[line_clicked_index]
+
+                    if x <= len(line) + self._cursor_max_left:
+                        old_text_pos = self._cursor_text_pos_x
+                        old_cursor_x = self._cursor_x
+                        self._cursor_x = x
+                        self._cursor_text_pos_x = old_text_pos + (x - old_cursor_x)
+                    else:
+                        self._cursor_x = self._cursor_max_left + len(line)
+                        self._cursor_text_pos_x = len(line)
+
+
+    def _handle_key_press(self, key_pressed: int) -> None:
         """Override of base class handle key press function
 
         Parameters
@@ -804,7 +890,7 @@ def _handle_key_press(self, key_pressed):
             key code of key pressed
         """
 
-        super()._handle_key_press(key_pressed)
+        Widget._handle_key_press(self, key_pressed)
 
         if key_pressed == py_cui.keys.KEY_LEFT_ARROW:
             self._move_left()
@@ -832,11 +918,11 @@ def _handle_key_press(self, key_pressed):
             self._insert_char(key_pressed)
 
 
-    def _draw(self):
+    def _draw(self) -> None:
         """Override of base class draw function
         """
 
-        super()._draw()
+        Widget._draw(self)
 
         self._renderer.set_color_mode(self._color)
         self._renderer.draw_border(self)
diff --git a/requirements_dev.txt b/requirements_dev.txt
index 906f317..ecfb67a 100644
--- a/requirements_dev.txt
+++ b/requirements_dev.txt
@@ -1,5 +1,6 @@
 pytest
 pytest-cov
+wheel
 npdoc2md
 mkdocs
-windows-curses ; platform_system=="Windows"
\ No newline at end of file
+windows-curses ; platform_system=="Windows"


Test patch:
diff --git a/.github/workflows/py_cui_test.yml b/.github/workflows/py_cui_test.yml
index 3fe7496..0155944 100644
--- a/.github/workflows/py_cui_test.yml
+++ b/.github/workflows/py_cui_test.yml
@@ -10,7 +10,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: [3.5, 3.6, 3.7, 3.8]
+        python-version: [3.6, 3.7, 3.8, 3.9]
 
     steps:
     - uses: actions/checkout@v2
diff --git a/tests/conftest.py b/tests/conftest.py
index 11603ce..1343096 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -56,7 +56,7 @@ def _PYCUI(rows, cols, height, width):
 def WIDGETSET(request, LOGGER):
 
     def _WIDGETSET(rows, cols, height, width):
-        return py_cui.widget_set.WidgetSet(rows, cols, LOGGER, simulated_terminal=[height, width])
+        return py_cui.widget_set.WidgetSet(rows, cols, LOGGER, root=py_cui.PyCUI(rows, cols), simulated_terminal=[height, width])
 
     return _WIDGETSET
 
@@ -128,4 +128,4 @@ def _COLORRULE(text, rule_type, match_type, color_A=py_cui.RED_ON_BLACK, color_B
         color_rule = py_cui.colors.ColorRule(text, color_A, color_B, rule_type, match_type, region, whitespace, LOGGER)
         return color_rule
 
-    return _COLORRULE
\ No newline at end of file
+    return _COLORRULE
diff --git a/tests/test_core/test_py_cui_core.py b/tests/test_core/test_py_cui_core.py
index 636efe4..c41a5f8 100644
--- a/tests/test_core/test_py_cui_core.py
+++ b/tests/test_core/test_py_cui_core.py
@@ -29,11 +29,11 @@ def test_add_scroll_menu(PYCUI):
     test_cui.add_scroll_menu('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.ScrollMenu)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -44,11 +44,11 @@ def test_add_checkbox_menu(PYCUI):
     test_cui.add_checkbox_menu('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.CheckBoxMenu)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -59,11 +59,11 @@ def test_add_label(PYCUI):
     test_cui.add_label('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.Label)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -74,11 +74,11 @@ def test_add_block_label(PYCUI):
     test_cui.add_block_label('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.BlockLabel)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -89,11 +89,11 @@ def test_add_text_box(PYCUI):
     test_cui.add_text_box('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.TextBox)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -104,11 +104,11 @@ def test_add_button(PYCUI):
     test_cui.add_button('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.Button)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -119,11 +119,11 @@ def test_add_text_block(PYCUI):
     test_cui.add_text_block('Demo', 1, 1)
     assert len(test_cui.get_widgets().keys()) == 1
     for key in test_cui.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_cui.get_widgets()['Widget0']
+    widget = test_cui.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.ScrollTextBlock)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
diff --git a/tests/test_core/test_widget_set.py b/tests/test_core/test_widget_set.py
index a1ad6a2..271c0dc 100644
--- a/tests/test_core/test_widget_set.py
+++ b/tests/test_core/test_widget_set.py
@@ -15,11 +15,11 @@ def test_add_scroll_menu(WIDGETSET):
     test_widget_set.add_scroll_menu('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.ScrollMenu)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -30,11 +30,11 @@ def test_add_checkbox_menu(WIDGETSET):
     test_widget_set.add_checkbox_menu('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.CheckBoxMenu)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -45,11 +45,11 @@ def test_add_label(WIDGETSET):
     test_widget_set.add_label('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.Label)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -60,11 +60,11 @@ def test_add_block_label(WIDGETSET):
     test_widget_set.add_block_label('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.BlockLabel)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -75,11 +75,11 @@ def test_add_text_box(WIDGETSET):
     test_widget_set.add_text_box('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.TextBox)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -90,11 +90,11 @@ def test_add_button(WIDGETSET):
     test_widget_set.add_button('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.Button)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
@@ -105,11 +105,11 @@ def test_add_text_block(WIDGETSET):
     test_widget_set.add_text_block('Demo', 1, 1)
     assert len(test_widget_set.get_widgets().keys()) == 1
     for key in test_widget_set.get_widgets().keys():
-        assert key == 'Widget0'
+        assert key == 0
         break
-    widget = test_widget_set.get_widgets()['Widget0']
+    widget = test_widget_set.get_widgets()[0]
     assert isinstance(widget, py_cui.widgets.ScrollTextBlock)
-    assert widget.get_id() == 'Widget0'
+    assert widget.get_id() == 0
     row, col = widget.get_grid_cell()
     assert row == 1
     assert col == 1
diff --git a/tests/test_ui_elements/test_statusbar.py b/tests/test_ui_elements/test_statusbar.py
index 8de7b68..20773d2 100644
--- a/tests/test_ui_elements/test_statusbar.py
+++ b/tests/test_ui_elements/test_statusbar.py
@@ -7,7 +7,7 @@
 
 
 def test_status_bar():
-    bar = py_cui.statusbar.StatusBar('Hello', py_cui.WHITE_ON_BLACK)
+    bar = py_cui.statusbar.StatusBar('Hello', py_cui.WHITE_ON_BLACK, root=py_cui.PyCUI)
     assert bar.get_text() == 'Hello'
     bar.set_text('Test')
     assert bar.get_text() == 'Test'