# egui
Talon's egui module provides Python bindings for [the egui graphics library](https://docs.rs/egui/latest/egui/index.html). See egui's official documentation for more features. 

## Hello World
This is an example of showing the text "Hello World" in an egui window.

```python
import egui
from talon.egui import Window

class HelloWorldWindow:
	def __init__(self):
		# set up a window object with the title `Hello World` 
		self.window = Window()
		self.window.title = "Hello World"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
	
	async def ui(self, ui: egui.ui) -> None:
		# show the text `Hello World`
		ui.label("Hello World")
		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
hello_world = HelloWorldWindow()
hello_world.show()
```

Talon displays an egui user interface inside a `Window`. The code above defines a window with title `Hello World` and then sets the contents to the ui method.

A function for showing egui widgets should be defined with the `async` keyword and take a `ui` object as an argument. Widgets can be drawn on the ui using methods. Keep in mind that the framework will call the ui function repeatedly at a rapid pace.

The line `ui.label("Hello World")` shows a label with the text `Hello World` on the ui.

This code will immediately show the window, but the hide and show methods could be used to control when the window is shown.

## Basic Widgets

This example and the following explanations show how to use some basic egui widgets.

```python
import egui
from talon.egui import Window

LAST_PAGE_NUMBER: int = 3

class WidgetExamplesWindow:
	def __init__(self):
		# set up a window object with the title `Widgets Examples` 
		self.window = Window()
		self.window.title = "Widgets Examples"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
		# define some values that will be manipulated by the widgets
		# egui.Mutable objects have a set and get method for changing and accessing the value respectively
		# this is used to let some widgets change the value in response to user input
		self.single_line_text_input = egui.Mutable("")
		self.multiline_text_input = egui.Mutable("This one allows multiple lines.\nThe other text input only allows a single line.")
		self.chosen_option = egui.Mutable(0)
		self.make_lowercase = egui.Mutable(False)
		self.numeric_value = egui.Mutable(10.0)
		self.page_number = 1
	
	async def ui(self, ui: egui.ui) -> None:
		# use the await keyword when invoking an async function
		# show a header showing the current page number and allowing changing the page
		await self.show_header(ui)
		# show the widgets corresponding to the page number
		if self.page_number == 1:
			await self.show_text_input_and_buttons_page(ui)
		elif self.page_number == 2:
			await self.show_option_picking_page(ui)
		elif self.page_number == 3:
			await self.show_numeric_input_page(ui)

	async def show_header(self, ui: egui.ui) -> None:
		ui.label(f"Page {self.page_number}")
		# separator() shows a line. this is useful for visually separating widgets
		ui.separator()
		# button for going to the next page
		# ui.button(...).clicked() is temporarily true when the user clicks the button
		if ui.button("Next Page").clicked() and self.page_number < LAST_PAGE_NUMBER:
			self.page_number += 1
		# button for going to the previous page
		if ui.button("Previous Page").clicked() and self.page_number > 1:
			self.page_number -= 1
		ui.separator()

	async def show_text_input_and_buttons_page(self, ui: egui.ui) -> None:
		ui.label("Text Input and Buttons")
		# this adds a widget for editing a single line of text
		# TextEdit.singleline() expects a Mutable wrapping a string
		ui.add(egui.TextEdit.singleline(self.single_line_text_input))
		# ui.add_space adds blank space, which can be useful for separating widgets
		ui.add_space(10)
		# access the values wrapped by a Mutable with .get()
		if self.make_lowercase.get():
			ui.label("Lowercase text:")
			ui.label(self.single_line_text_input.get().lower())
		else:
			ui.label("Uppercase text:")
			ui.label(self.single_line_text_input.get().upper())
		# add a smaller amount of blank space this time
		ui.add_space(5)
		# add a checkbox. Use a Mutable wrapping a boolean for the first argument
		# and the label text as the second argument
		ui.checkbox(self.make_lowercase, "Show the text in lowercase")
		# this is an example of using a button to clear the text stored in self.single_line_text_input
		# ui.button(...).clicked() is temporarily true when the user clicks the button
		if ui.button("Clear Text").clicked():
			self.single_line_text_input.set("")
		# separator() shows a line. this is useful for visually separating widgets
		ui.separator()
		ui.label("Multiline text area")
		# this adds a widget for editing text that can have multiple lines
		# TextEdit.multiline() expects a Mutable wrapping a string
		ui.add(egui.TextEdit.multiline(self.multiline_text_input))

	async def show_option_picking_page(self, ui: egui.ui) -> None:
		"""This page shows some ways to let a user pick an option from a list of options"""
		ui.label("Option Picking")
		ui.separator()

		for i in range(1, 4):
			# a selectable label needs two arguments: 
			#  a boolean for if it should be highlighted to indicate that its option was selected
			#  a string to display
			is_selected = self.chosen_option.get() == i
			label_text = f"Choose {i}"
			if ui.selectable_label(is_selected, label_text).clicked():
				# logic for what to do if the option is selected goes here
				self.chosen_option.set(i)

		ui.add_space(5)
		
		for i in range(1, 4):
			# a radio button works similarly to a selectable label but
			# shows a circle by the label, and the circle is filled in when the boolean is true
			is_selected = self.chosen_option.get() == i
			label_text = f"Choose {i}"
			if ui.radio(is_selected, label_text).clicked():
				self.chosen_option.set(i)

		ui.add_space(5)

		# clicking a combo box shows options in a dropdown
		combo_box_label = "Pick an option"
		currently_chosen_option = str(self.chosen_option.get())
		async with (
			# the label is displayed by the combo box
			egui.ComboBox.from_label(combo_box_label)
			# when the dropdown is collapsed, it shows the selected text
			.selected_text(currently_chosen_option)
			.show()
		) as combo_box:
			if combo_box.shown:
				combo_ui = combo_box.ui
				for i in range(1, 4):
					# the arguments for the selectable_value are
					#  a Mutable to set when the option is clicked
					#  the value corresponding to the option
					#  the string to display for the option in the dropdown
					label_text = f"Choose {i}"
					combo_ui.selectable_value(self.chosen_option, i, label_text)

	async def show_numeric_input_page(self, ui: egui.ui) -> None:
		"""This page shows some ways to let users input numerical values"""
		ui.label("Number Input")
		ui.separator()
		# to create a slider, give it
		# a Mutable containing the numeric value it should control
		# the minimum acceptable value
		# and the maximum acceptable value
		# by default, this constrains the Mutable value to that range even if changed through other widgets
		# you can use the .text() method to add a label
		# by default, a slider shows the value next to it in an editable text field 
		#    that the user can use instead of the slider
		minimum_acceptable_value = 0.0
		maximum_acceptable_value = 100.0
		slider_label_text = "Value"
		slider = egui.Slider(
			self.numeric_value,
			minimum_acceptable_value,
			maximum_acceptable_value).text(slider_label_text)
		ui.add(slider)

		# a drag value widget displays a numeric value and allows changing it by either 
		#  dragging the cursor starting at the widget
		#  or clicking it and then editing the number text
		ui.add(egui.DragValue(self.numeric_value))
			
		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
widget_window = WidgetExamplesWindow()
widget_window.show()
```

### Mutable
A Mutable object wraps a value so it can be set with .set() and accessed with .get(). This is used by widgets that set a value based on user input. The following example uses a Mutable to wrap a string editable through a text field.

```python
		...
		self.single_line_text_input = egui.Mutable("")
		...

		ui.add(egui.TextEdit.singleline(self.single_line_text_input))
		ui.add_space(10)
		ui.label("Lowercase text:")
		ui.label(self.single_line_text_input.get().lower())
		...
```

### Button
You create a button with `ui.button()` and a string argument for the button's label. This returns an object you can use to check if the button has been clicked with `.clicked()`. The following example code sets a Mutable to the empty string when a button is clicked.

```python
	...
	if ui.button("Clear Text").clicked():
		self.single_line_text_input.set("")
	...
```

### Label
You create a label with `ui.label()` with a string argument for the label text. This shows text but does not allow editing it.

Example:

```python
	...
	ui.label(f"Page {self.page_number}")
	...
```

### Checkbox
A checkbox allows toggling a Mutable wrapped boolean. You create a checkbox with `ui.checkbox()`. 

Arguments:

1: a Mutable wrapping a boolean

2: a string for the label text

Example:

```python
	...
	self.make_lowercase = egui.Mutable(False)
	...
	if self.make_lowercase.get():
		...
	else:
		...
	ui.checkbox(self.make_lowercase, "Show the text in lowercase")
	...
```

### Single Line Text Field
You create a single line text field for editing text with `ui.add(egui.TextEdit.singleline(...))`. TextEdit.singleline takes a Mutable wrapped string as its argument. 

Example:

```python
	...
	self.single_line_text_input = egui.Mutable("")
	...
	ui.add(egui.TextEdit.singleline(self.single_line_text_input))
	ui.label(self.single_line_text_input.get().lower())
	...
```

### Multiline Text Field
You create a multiline text field for editing text with `ui.add(egui.TextEdit.multiline(...))`. TextEdit.multiline takes a Mutable wrapped string as its argument.

```python
	...
	self.multiline_text_input = egui.Mutable("This one allows multiple lines.\nThis is another line.")
	...
	ui.add(egui.TextEdit.multiline(self.multiline_text_input))
	...
```

### Separator
You create a separator with `ui.separator()` to visually separate widgets with a line. 

Example:

```python
		...
		ui.label("This Is a Title")
		ui.separator()
		ui.label("this is clearly separated from the title")
		...
```

### Space
You can create space in between widgets to visually separate them with `ui.add_space()`. This takes the amount of space to add between the widgets as an argument.

Example:

```python
		...
		ui.label("This Is a Title")
		ui.add_space(10)
		ui.label("this is clearly separated from the title")
		...
```

### Selectable Label
A selectable label is a text label that is highlighted when a boolean argument passed to it is true. These can be useful for letting users pick options. You create one with `ui.selectable_label()`. This returns an object you can use to check if the selectable_label has been clicked with `.clicked()`.

Arguments:

1: a boolean indicating if it is currently selected

2: a string for the label text

Example:

```python
		...
		for i in range(1, 4):
			is_selected = self.chosen_option.get() == i
			label_text = f"Choose {i}"
			if ui.selectable_label(is_selected, label_text).clicked():
				self.chosen_option.set(i)
		...
```

### Radio Button
You can create a radio button with`ui.radio()`. A radio button shows a circle by a label. The circle is filled in when the boolean passed to it is true. This returns an object you can use to check if the radio button has been clicked with `.clicked()`.

Arguments:

1: a boolean indicating if it is currently selected

2: a string for the label text

Example:

```python
		...
 		for i in range(1, 4):
			is_selected = self.chosen_option.get() == i
			label_text = f"Choose {i}"
			if ui.radio(is_selected, label_text).clicked():
				self.chosen_option.set(i)
		...
```


### Combo Box
A combo box allows showing options in a dropdown. 

You can create a combo box with `egui.ComboBox.from_label()` with a string argument for the label. You can call the `.selected_text()` method with a string argument for the currently selected text to display it when the dropdown is collapsed.

To show the combo box, call `.show()` wrapped within `async with (...) as combo_box:`. You can use another variable name instead of `combo_box`.

You can use `combo_box.shown` to check if the combo box is being shown and `combo_box.ui` to access a ui that can be used to show widgets inside the combo box.

One way to show an option inside a combo box is with `combo_box.ui.selectable_value()`.

Arguments for `selectable_value`:

1: A Mutable to set when the option is clicked

2: the value corresponding to the option

3: the string to display for the option in the dropdown


Example:

```python
	...
	combo_box_label = "Pick an option"
	currently_chosen_option = str(self.chosen_option.get())
	async with (
		egui.ComboBox.from_label(combo_box_label)
		.selected_text(currently_chosen_option)
		.show()
	) as combo_box:
		if combo_box.shown:
			combo_ui = combo_box.ui
			for i in range(1, 4):
				label_text = f"Choose {i}"
				option_value = i
				combo_ui.selectable_value(self.chosen_option, option_value, label_text)
	...
```

### Slider
You create a slider for adjusting a numeric value with `egui.Slider()` and then add it to a ui with `ui.add()`. By default, this constrains the numeric value even if the value is edited with another widget.

Arguments:

1: a Mutable wrapping a numeric value

2: the lower bound as a float

3: the upper bound as a float

You can call `.text(string)` on the slider to add an optional label.

Example:

```python
		...
		self.numeric_value = egui.Mutable(10.0)
		...
		minimum_acceptable_value = 0.0
		maximum_acceptable_value = 100.0
		slider_label_text = "Value"
		slider = egui.Slider(
			self.numeric_value,
			minimum_acceptable_value,
			maximum_acceptable_value).text(slider_label_text)
		ui.add(slider)
		...
```

### Drag Value
A Drag Value widget shows a numeric value and allows changing the value through either (1) dragging the widget or (2) clicking the widget and then typing a new value. You create a Drag Value with `egui.DragValue()` and then add it to a ui with `ui.add()`. `egui.DragValue()` takes a Mutable wrapping a numeric value as its argument.

Example:

```python
		...
		self.numeric_value = egui.Mutable(10.0)
		...
		ui.add(egui.DragValue(self.numeric_value))
		...
```


## Layouts
Layouts allow deciding how widgets are arranged. `ui.horizontal()` and `ui.vertical()` allow making widgets arranged horizontally and vertically respectively. `Layout` objects can be used with `ui.with_layout` for more control. 

### Layouts Example
The following example uses a horizontal layout, a vertical layout, and a custom layout.


```python
import egui
from talon.egui import Window

class LayoutWindow:
	def __init__(self):
		# set up a window object with the title `Layouts` 
		self.window = Window()
		self.window.title = "Layouts"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
		
		self.text = egui.Mutable("")
	
	async def ui(self, ui: egui.ui) -> None:
		# use a horizontal layout for some widgets
		async with ui.horizontal():
			ui.label("Text Area:")
			ui.add(egui.TextEdit.singleline(self.text))
			# use a vertical layout within the horizontal layout
			# this appears on the right of the horizontal layout
			# but goes top down
			async with ui.vertical():
				ui.label("Title")
				ui.separator()
				ui.label("These are added vertically")
		
		# define a layout that goes from right to left, does not try to wrap, and is aligned at the bottom
		layout = (
			egui.Layout.right_to_left(egui.Align.Max)
				.with_main_wrap(False)
		)

		# show widgets inside that layout
		async with ui.with_layout(layout):
			ui.add_space(10)
			ui.label("Layout Start")
			ui.separator()
			ui.label("Next Bottom Layout Label")
			ui.add_space(20)
			ui.label("c"*90)

		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
layout_window = LayoutWindow()
layout_window.show()
```

### Creating Layout Objects
A Layout object has the following fields:
 - main_dir: Direction. The main direction of the layout, such as left to right.
 - main_wrap: bool. Decides if components should wrap along the main direction.
 - main_align: Align. Decides the alignment of the main axis, such as aligning components at the top or bottom of each row.
 - main_justify: bool. Decides if the main axis should be justified.
 - cross_align: Align. Decides the alignment of the cross axis.
 - cross_justify: bool. Decides if the cross axis should be justified.

An Align object has the following possible values:
 - `egui.Align.Min` (equivalent to `egui.Align.TOP` and `egui.Align.LEFT`)
 - `egui.Align.Center`
 - `egui.Align.Max` (equivalent to `egui.Align.BOTTOM` and `egui.Align.RIGHT`)

It is usually convenient to create Layout objects using chainable construction methods.

#### Layout Construction Methods

| method | arguments | description |
|--------|-----------|--------------------------------------------------------|
| left_to_right | (vertical_alignment: Align) | Place widgets from left to right. |
| right_to_left | (vertical_alignment: Align) | Place widgets from right to left. |
| top_down | (horizontal_alignment: Align) | Place widgets from top to bottom |
| bottom_up | (horizontal_alignment: Align) | Place widgets from bottom to top |
| with_main_wrap | (main_wrap: bool) | sets main_wrap |
| with_main_align | (main_align: Align) | sets main_align |
| with_cross_align | (cross_align: Align) | sets cross_align |
| with_main_justify | (main_justify: bool) | sets main_justify |
| with_cross_justify | (cross_justify: bool) | sets cross_justify |


For more information on creating Layout objects, [see the rust egui Layout documentation](https://docs.rs/egui/latest/egui/struct.Layout.html).

### Grids
Grids arrange widgets in a series of cells organized within rows and columns going left to right and top to bottom. Cell contents are left and center aligned by default.

You must group multiple widgets with a layout or container to put them in the same cell.

#### Grid Example

This is an example of how to create an egui Grid.

```python
import egui
from talon.egui import Window

class GridWindow():
	def __init__(self):
		# set up a window object with the title `Grid Example` 
		self.window = Window()
		self.window.title = "Grid Example"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
		
		# values for the grid
		self.num_cols = 3
		self.num_rows = 4
		self.min_col_width = 150.0
		self.max_col_width = 300.0
		self.min_row_height = 40.0

	async def ui(self, ui: egui.ui) -> None:
		# Create the grid
		# this identifier must be unique and is not displayed
		locally_unique_identifier = "my_grid"
		grid = (
			egui.Grid(locally_unique_identifier)
				.min_col_width(self.min_col_width)
				.max_col_width(self.max_col_width)
				.min_row_height(self.min_row_height)
		)
		# Show the grid
		async with grid.show() as grid_ui:
			for row in range(self.num_rows):
				for col in range(self.num_cols):
					grid_ui.label(f"Row {row+1}, Col {col+1}")
				# tell the grid to start on the next row
				grid_ui.end_row()

	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
grid_window = GridWindow()
grid_window.show()
```

#### Creating Grid Objects

Create a Grid object with the `egui.Grid` constructor using a locally unique string as the argument.

#### Grid Construction Methods
You configure a Grid with chainable construction methods similar to Layout objects.

| method | arguments | description |
|--------|-----------|------|
| `num_columns` | (number_of_columns: int) | This lets the last column take up the rest of the available space. |
| `min_col_width` | (minimum_column_width: float) | Set the minimum column width. |
| `max_col_width` | (maximum_column_width: float) | Set the maximum column width. |
| `spacing` | (spacing: egui.Vec2) | Set the amount of space between columns and rows. |
| `min_row_height` | (minimum_row_height: float) | Set the minimum row height. |

#### Showing a Grid
To show a Grid, call `.show()` wrapped within `async with (...) as grid_ui:`. You can use another variable name instead of `grid_ui`. You can then create widgets inside the Grid's ui as if it was a regular ui.

#### More Information
For more information on Grids, [see the rust egui Grid documentation](https://docs.rs/egui/latest/egui/struct.Grid.html).


## Styling
egui allows customizing the style of widgets and text. You can set a default style and also override it temporarily.

The following example shows setting the font size and text color. [See egui's style documentation for more options](https://docs.rs/egui/latest/egui/style/index.html).

```python
import egui
from talon.egui import Window

class HelloWorldWindow:
	def __init__(self):
		# set up a window object with the title `Hello World` 
		self.window = Window()
		self.window.title = "Hello World"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
	
	async def ui(self, ui: egui.ui) -> None:
		# set the default style
		self.set_style(ui, 30)
		# show the text `Hello World`
		ui.label("Hello World")

		# draw a gray label with smaller font size
		async with ui.scope() as scope:
			async with scope.style_mut() as style:
				font_size = 10
				font_id = egui.FontId(font_size, egui.FontFamily.Proportional)
				style.override_font_id = font_id
				visuals = style.visuals()
				visuals.override_text_color = egui.Color32.gray()
				style.set_visuals(visuals)
				scope.label("Hello World")

	def set_style(self, ui: egui.ui, font_size: int) -> None:
		# set the font size and color
		# get the current style
		style = ui.style()
		# create a new Font
		font_id = egui.FontId(font_size, egui.FontFamily.Proportional)
		visuals = style.visuals()
		# make the font color green
		visuals.override_text_color = egui.Color32.green()
		style.set_visuals(visuals)
		# change the style font
		style.override_font_id = font_id
		# update the style
		ui.set_style(style)
		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
hello_world = HelloWorldWindow()
hello_world.show()
```


## Responses
ui methods for adding widgets to the ui return a [Response object](https://docs.rs/egui/latest/egui/response/struct.Response.html). A Response provides methods for responding to user interactions with widgets. You have already seen this used to handle button click events. 

Useful Response methods include:
 - clicked. Returns True if the user left clicked the widget this frame.
 - secondary_clicked. Returns True if the user right clicked the widget this frame.
 - double_clicked. Returns True if the user double left clicked the widget this frame.
 - hovered. Returns True if the cursor is hovering over the widget this frame.
 - has_focus. Returns True if the widget is focused.
 - on_hover_text. Takes a String argument and shows the string when the widget is hovered and enabled. 
 - on_disabled_hover_text. Works the same as on_hover_text but shows the string when the widget is hovered and disabled.

The following example adds hover text to the "Hello World" label.

```python
import egui
from talon.egui import Window

class HelloWorldWindow:
	def __init__(self):
		# set up a window object with the title `Hello World` 
		self.window = Window()
		self.window.title = "Hello World"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
	
	async def ui(self, ui: egui.ui) -> None:
		# show the text `Hello World`
		ui.label("Hello World").on_hover_text("You Have Been Greeted")
		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
hello_world = HelloWorldWindow()
hello_world.show()
```

## Containers
A container wraps widget(s). Useful containers include ScrollAreas, CollapsingHeaders, and Panels. See the [egui containers documentation for more](https://docs.rs/egui/latest/egui/containers/index.html).

Example:

```python
import egui
from talon.egui import Window

class GridWindow():
	def __init__(self):
		# set up a window object with the title `Containers Example` 
		self.window = Window()
		self.window.title = "Containers Example"
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)
		
	async def ui(self, ui: egui.ui) -> None:
		ui.label("Scroll Area (put mouse over it to scroll):")
		# define a vertical scroll area with limited height
		maximum_height = 200.0
		scroll_area = egui.ScrollArea.vertical().max_height(maximum_height)
		# show the scroll area
		async with scroll_area.show() as scroll_ui:
			# create widgets on the scroll ui
			for i in range(200):
				scroll_ui.label(str(i))

		# create a collapsing header
		# this can be collapsed and expanded by clicking the header
		collapsing_header_title = "Collapsing Header Title"
		collapsing_header = egui.CollapsingHeader(collapsing_header_title)
		async with collapsing_header.show() as collapsing_ui:
			# create widgets on the header ui
			collapsing_ui.label("This is the body")

		# create panels
		# these put the contained widgets at different parts of the screen
		
		ui.label("Panels")
		ui.separator()
		# globally unique id
		top_panel_id = "top_panel"
		# create the panel
		top_panel = egui.Panel.top(top_panel_id)
		# show the panel
		async with top_panel.show(ui.ctx()) as panel_ui:
			# create widgets on the panel ui
			panel_ui.label("Top")

		# the bottom, left, and right panels work the same
		bottom_panel_id = "bottom_panel"
		bottom_panel = egui.Panel.bottom(bottom_panel_id)
		async with bottom_panel.show(ui.ctx()) as panel_ui:
			panel_ui.label("Bottom")

		left_panel_id = "left_panel"
		left_panel = egui.Panel.left(left_panel_id)
		async with left_panel.show(ui.ctx()) as panel_ui:
			panel_ui.label("Left")

		right_panel_id = "right_panel"
		right_panel = egui.Panel.right(right_panel_id)
		async with right_panel.show(ui.ctx()) as panel_ui:
			panel_ui.label("Right")
			panel_ui.label("A second line on the right")

		# IMPORTANT: always add a central panel last
		# the central panel does not need an argument and is created with a different method
		# but otherwise works the same
		central_panel = egui.CentralPanel.default()
		async with central_panel.show(ui.ctx()) as panel_ui:
			panel_ui.label("Center")

	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
grid_window = GridWindow()
grid_window.show()
```

### ScrollArea
A scroll area lets the user scroll vertically and/or horizontally to navigate contained widgets.

You create a ScrollArea with `egui.ScrollArea.vertical()` to create a ScrollArea that can be vertically scrolled, `egui.ScrollArea.horizontal()` to create a ScrollArea that can be horizontally scrolled, and `egui.ScrollArea.both()` to create a ScrollArea that can be both horizontally and vertically scrolled.

You can customize a ScrollArea with chainable methods including:

| method | arguments | description |
|--------|-----------|--------------------------------------------------------|
| max_width | (max_width: float) | Set the maximum width of the scroll area. |
| max_height | (max_height: float) | Set the maximum height of the scroll area. |
| auto_shrink | (bool) | Determines if the scroll area should shrink to fit the contents if small. |

To show a ScrollArea, call `.show()` wrapped within `async with (...) as scroll_ui:`. You can use another variable name instead of `scroll_ui`. You can then create widgets inside the ScrollArea's ui as if it was a regular ui.

See the egui [ScrollArea documentation for more information and customization options](https://docs.rs/egui/latest/egui/containers/scroll_area/struct.ScrollArea.html).

### CollapsingHeader
Users can expand and collapse the widgets wrapped by a CollapsingHeader by clicking on the label at the top of the header. To create a CollapsingHeader, use `egui.CollapsingHeader()` with a string for the top label text as the argument. 

You can customize a CollapsingHeader with chainable methods including:

| method | arguments | description |
|--------|-----------|--------------------------------------------------------|
| default_open | (open: bool) | Sets if the CollapsingHeader should be expanded by default |
| open | (open: bool) | Sets if the CollapsingHeader should be expanded this frame |

To show a CollapsingHeader, call `.show()` wrapped within `async with (...) as collapsing_ui:`. You can use another variable name instead of `collapsing_ui`. You can then create widgets inside the CollapsingHeader's ui as if it was a regular ui.

For more information, see the [egui documentation for CollapsingHeaders](https://docs.rs/egui/latest/egui/containers/collapsing_header/struct.CollapsingHeader.html)

### Panels
Panels allow putting widgets on different parts of the ui, including the left, right, top, bottom, and center. 

Use one of the following functions to create a panel. Note that a CentralPanel should always be added after the other panels.

| function | argument | description |
| --- | --- | --- |
| egui.Panel.top | a String representing a unique id | a panel on the top of the ui |
| egui.Panel.bottom | a String representing a unique id | a panel on the bottom of the ui |
| egui.Panel.left | a String representing a unique id | a panel on the left of the ui |
| egui.Panel.right | a String representing a unique id | a panel on the right of the ui |
| egui.CentralPanel.default | N/A | a panel in the center of the ui |

To show a Panel with variable name "panel" within a ui named "ui", use `async with (panel.show(ui.ctx())) as panel_ui:`. You can use another variable name instead of `panel_ui`. You can then create widgets inside the Panel's ui as if it was a regular ui.

For more information, see the [egui documentation for Panels](https://docs.rs/egui/latest/egui/containers/panel/struct.Panel.html). 

## Window Customization
Recall that a Window is what contains the ui. Window object properties you can set include the following:

| property name | type | description |
| --- | --- | --- |
| title | str | The title to show at the top of the window |
| rect | skia.Rect | The window's rectangle setting its initial size and position |
| decorated | bool | Decides if the window should have a surrounding border including buttons |
| toplevel | bool | Decides if the window should be toplevel, which means it is never hidden behind another window |
| autosize | bool | Decides if the window should set its own size based on the contents |
| draggable | bool | Decides if you can move the window by dragging on the window background |

Example:

```python
import egui
import skia
from talon.egui import Window

class HelloWorldWindow:
	def __init__(self):
		# set up a window object 
		self.window = Window()
		# set the window title
		# this does nothing meaningful because decorated is set to false
		self.window.title = "irrelevant"
		# make the window top level
		self.window.toplevel = True
		# make the window undecorated
		self.window.decorated = False
		# do not auto size the window
		self.window.autosize = False
		# set the window rectangle
		x = 10
		y = 10
		width = 200
		height = 40
		self.window.rect = skia.Rect(x, y, width, height)
		# make the window show the contents defined by the ui method
		self.window.set_content(self.ui)

	
	async def ui(self, ui: egui.ui) -> None:
		# show the text `Hello World`
		ui.label("Hello World")
		if ui.button("Close Window").clicked():
			self.hide()
		
	def hide(self) -> None:
		"""Hide the window"""
		self.window.hide()

	def show(self) -> None:
		"""Show the window"""
		self.window.show()

# define and show the window after the file is saved
hello_world = HelloWorldWindow()
hello_world.show()
```

## Understanding How to Write Python Based on the Rust Documentation
Referencing the [official egui library documentation](https://docs.rs/egui/latest/egui/index.html) is useful when working with the Python bindings, but keep in mind that the library was written in Rust, which has different syntax. 

Use "." in Python instead of "::". 

Use a Mutable object in Python instead of "&mut ".

"Typename::new()" is how a constructor is typically written in Rust. You should instead use "Typename()" to create an instance of "Typename" in Python.

Rust denotes code blocks with curly braces.

"||" at the start of a code block inside a function call in Rust denotes a closure, which is similar to a Python lambda expression. Instead of putting a lambda inside the corresponding function call, in the Python bindings you write "async with call_goes_here() as variable_that_was_originally_in_the_vertical_bars_goes_here:". 

### Examples
The following examples show transforming Rust from the official egui documentation to Talon's Python bindings.

The following in Rust

```rust
ui.add(egui::Label::new("Hello World!"));
```

becomes the following in Python

```python
ui.add(egui.Label("Hello World!"))
```

This is because `egui::Label` becomes `egui.Label` and `Label::new()` becomes `Label()`. 

The following in Rust

```rust
ui.horizontal(|ui| {
    ui.label("Add widgets");
    if ui.button("on the same row!").clicked() {
        /* … */
    }
});
```

becomes the following in Python

```python
async with ui.horizontal():
    ui.label("Add widgets")
    if ui.button("on the same row!").clicked():
        pass
```

The closure `|ui| {}` was converted to `async with ui.horizontal():`. We could have written this as `async with ui.horizontal() as horizontal_ui:`, but we can just reuse the original "ui" variable when using layouts.

The following in Rust

```rust
let enabled = true;
...
ui.checkbox(&mut enabled, "Enable subsection");
```

becomes the following in Python

```python
enabled = egui.Mutable(True)
...
ui.checkbox(enabled, "Enable subsection")
```

Because the function call included "&mut" before the "enabled" argument, the corresponding Python uses a Mutable object for the argument.