Skip to content

Commit 286fcc2

Browse files
committed
Add documentation for expand panels and inputs.
1 parent a3171d8 commit 286fcc2

File tree

2 files changed

+630
-0
lines changed

2 files changed

+630
-0
lines changed

doc/11-Expand-Panel.md

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
--------------------
2+
# 11 Expand Panel
3+
--------------------
4+
5+
An expand panel is a collapsible container that can show or hide its
6+
content. It consists of a clickable header (which always displays) and a
7+
content area (which toggles visibility based on the `open` state). The
8+
header displays an arrow icon that indicates the current state:
9+
`icon_arrow_up` when open, `icon_arrow_down` when closed.
10+
11+
See also: `examples/expand_panel.v` for a runnable showcase.
12+
13+
## Overview
14+
15+
`expand_panel` creates a container with two parts:
16+
17+
- **Header** (`head`): A clickable row that contains custom content and
18+
an arrow indicator. Clicking the header toggles the panel's open
19+
state.
20+
- **Content** (`content`): A collapsible column that shows or hides
21+
based on the `open` boolean.
22+
23+
The expand panel is built from nested containers with separate styling
24+
for the border and interior, similar to buttons. The header has hover
25+
effects (cursor changes to pointing hand, background color changes on
26+
hover).
27+
28+
## ExpandPanelCfg
29+
30+
The `expand_panel` view is created with an `ExpandPanelCfg` structure.
31+
Important fields:
32+
33+
- `id string` --- Optional identifier for the view.
34+
- `head View` --- The header content (required). This is a view that
35+
appears in the clickable header row. The arrow icon is automatically
36+
added to the right side.
37+
- `content View` --- The collapsible content (required). This view is
38+
shown when `open: true` and hidden when `open: false`.
39+
- `open bool` --- Controls whether the panel is expanded (`true`) or
40+
collapsed (`false`).
41+
- `on_toggle fn (mut Window)` --- Callback invoked when the header is
42+
clicked. Use this to update your state (typically toggle the `open`
43+
field in your app state).
44+
- Colors
45+
- `color` --- Interior color (defaults to theme's
46+
`expand_panel_style.color`)
47+
- `color_border` --- Border color (defaults to theme's
48+
`expand_panel_style.color_border`)
49+
- Padding and radius
50+
- `padding Padding` --- Interior padding (defaults to theme's
51+
`expand_panel_style.padding`)
52+
- `padding_border Padding` --- Border padding (defaults to theme's
53+
`expand_panel_style.padding_border`)
54+
- `radius f32` --- Corner radius for interior (defaults to theme's
55+
`expand_panel_style.radius`)
56+
- `radius_border f32` --- Corner radius for border (defaults to
57+
theme's `expand_panel_style.radius_border`)
58+
- Sizing and layout
59+
- `sizing Sizing` --- Standard sizing (`fit`, `fill`, `fixed`,
60+
combinations)
61+
- `min_width`, `max_width`, `min_height`, `max_height f32` --- Size
62+
constraints
63+
- `fill bool` --- Fill interior rectangle (defaults to theme's
64+
`expand_panel_style.fill`)
65+
- `fill_border bool` --- Fill border rectangle (defaults to theme's
66+
`expand_panel_style.fill_border`)
67+
68+
## Interaction model
69+
70+
- **Click**: Clicking anywhere in the header row triggers `on_toggle` if
71+
provided.
72+
- **Hover**: When the pointer is over the header, the mouse cursor
73+
changes to a pointing hand and the header background uses
74+
`gui_theme.color_hover`.
75+
- **Arrow indicator**: The header automatically shows `icon_arrow_up`
76+
when `open: true` and `icon_arrow_down` when `open: false`.
77+
78+
## Basic example
79+
80+
A simple expand panel that toggles when clicked:
81+
82+
``` v
83+
import gui
84+
85+
struct App {
86+
mut:
87+
panel_open bool
88+
}
89+
90+
fn main() {
91+
mut window := gui.window(
92+
title: 'Expand Panel Demo'
93+
state: &App{}
94+
width: 400
95+
height: 300
96+
on_init: fn (mut w gui.Window) {
97+
w.update_view(main_view)
98+
}
99+
)
100+
window.run()
101+
}
102+
103+
fn main_view(mut w gui.Window) gui.View {
104+
mut app := w.state[App]()
105+
106+
return gui.column(
107+
padding: gui.theme().padding_medium
108+
content: [
109+
gui.expand_panel(
110+
open: app.panel_open
111+
head: gui.row(
112+
padding: gui.theme().padding_medium
113+
content: [gui.text(text: 'Click to expand')]
114+
)
115+
content: gui.column(
116+
padding: gui.theme().padding_medium
117+
content: [
118+
gui.text(text: 'This is the hidden content.'),
119+
gui.text(text: 'It appears when the panel is open.'),
120+
]
121+
)
122+
on_toggle: fn (mut win gui.Window) {
123+
mut app := win.state[App]()
124+
app.panel_open = !app.panel_open
125+
}
126+
),
127+
]
128+
)
129+
}
130+
```
131+
132+
## Multiple panels with auto-close
133+
134+
A common pattern is to have multiple expand panels where opening one
135+
closes the others:
136+
137+
``` v
138+
struct App {
139+
mut:
140+
open_titles []string
141+
auto_close bool
142+
}
143+
144+
fn expander(title string, description string, mut app App) gui.View {
145+
return gui.expand_panel(
146+
open: title in app.open_titles
147+
sizing: gui.fill_fit
148+
head: gui.row(
149+
padding: gui.theme().padding_medium
150+
content: [gui.text(text: title)]
151+
)
152+
content: gui.column(
153+
padding: gui.theme().padding_medium
154+
content: [
155+
gui.text(text: description, mode: .wrap),
156+
]
157+
)
158+
on_toggle: fn [title] (mut w gui.Window) {
159+
mut app := w.state[App]()
160+
if app.auto_close {
161+
// Close all others, open only this one
162+
match title in app.open_titles {
163+
true { app.open_titles.clear() }
164+
else { app.open_titles = [title] }
165+
}
166+
} else {
167+
// Toggle this one independently
168+
match title in app.open_titles {
169+
true { app.open_titles = app.open_titles.filter(it != title) }
170+
else { app.open_titles << title }
171+
}
172+
}
173+
}
174+
)
175+
}
176+
```
177+
178+
## Custom header content
179+
180+
The header can contain any views. Here's an example with multiple
181+
elements:
182+
183+
``` v
184+
gui.expand_panel(
185+
open: app.is_open
186+
head: gui.row(
187+
padding: gui.theme().padding_medium
188+
sizing: gui.fill_fit
189+
v_align: .middle
190+
content: [
191+
gui.text(text: 'Section Title', text_style: gui.theme().n3),
192+
gui.row(sizing: gui.fill_fit), // Spacer
193+
gui.text(text: 'Subtitle', text_style: gui.theme().n4),
194+
]
195+
)
196+
content: gui.column(
197+
content: [/* your content */]
198+
)
199+
on_toggle: fn (mut w gui.Window) { /* ... */ }
200+
)
201+
```
202+
203+
The arrow icon is automatically added to the right side of the header,
204+
so you don't need to include it in your `head` view.
205+
206+
## Styling
207+
208+
Expand panels use theme defaults, but you can override colors, padding,
209+
and radius:
210+
211+
``` v
212+
gui.expand_panel(
213+
open: app.open
214+
color: gui.rgb(240, 240, 240)
215+
color_border: gui.rgb(200, 200, 200)
216+
padding: gui.padding_medium
217+
padding_border: gui.padding_small
218+
radius: 8
219+
radius_border: 10
220+
head: gui.row(/* ... */)
221+
content: gui.column(/* ... */)
222+
on_toggle: fn (mut w gui.Window) { /* ... */ }
223+
)
224+
```
225+
226+
## Tips
227+
228+
- Store the `open` state in your app state and update it in `on_toggle`.
229+
- The header is always visible and clickable; only the content area
230+
toggles.
231+
- Use `invisible: !cfg.open` internally; the content container is hidden
232+
when closed, so it doesn't take up space.
233+
- For accordion-style behavior (only one panel open at a time), maintain
234+
a list of open panel IDs and clear/update it in `on_toggle`.
235+
- The arrow icon automatically switches between `icon_arrow_up` (open)
236+
and `icon_arrow_down` (closed) based on the `open` field.
237+
238+
## See also
239+
240+
- `03-Views.md` --- background on views, containers, and sizing
241+
- `07-Buttons.md` --- similar styling patterns (border/interior)
242+
- `08-Container-View.md` --- understanding the underlying container
243+
structure
244+
- `examples/expand_panel.v` --- comprehensive runnable examples

0 commit comments

Comments
 (0)