Skip to content

Commit 3fd7cfc

Browse files
mike-wardclaude
andcommitted
docs: add CONTAINERS.md; wire into doc_viewer and showcase
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent eb45982 commit 3fd7cfc

4 files changed

Lines changed: 378 additions & 38 deletions

File tree

docs/CONTAINERS.md

Lines changed: 368 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,368 @@
1+
# Containers
2+
3+
Containers are the building blocks of every v-gui layout. They hold
4+
other views, control how children are arranged, and provide sizing,
5+
alignment, scrolling, and styling options.
6+
7+
All containers share a single configuration struct — `ContainerCfg`
8+
so every option described below applies to `column`, `row`, `wrap`,
9+
`canvas`, and `circle` alike.
10+
11+
## Container Types
12+
13+
### `column`
14+
15+
Arranges children **top to bottom**. Gaps between items are set with
16+
`spacing`.
17+
18+
```v ignore
19+
gui.column(
20+
spacing: 8
21+
content: [
22+
gui.text(text: 'First'),
23+
gui.text(text: 'Second'),
24+
gui.text(text: 'Third'),
25+
]
26+
)
27+
```
28+
29+
### `row`
30+
31+
Arranges children **left to right**.
32+
33+
```v ignore
34+
gui.row(
35+
spacing: 8
36+
v_align: .middle
37+
content: [
38+
gui.text(text: 'Label'),
39+
gui.button(content: [gui.text(text: 'OK')]),
40+
]
41+
)
42+
```
43+
44+
### `wrap`
45+
46+
Arranges children left to right and **flows to the next line** when the
47+
container width is exceeded. Resize the window to see items reflow.
48+
Sugar for `row(wrap: true, ...)`.
49+
50+
```v ignore
51+
gui.wrap(
52+
sizing: gui.fill_fit
53+
spacing: 8
54+
content: [
55+
gui.checkbox(label: 'Alpha', ...),
56+
gui.checkbox(label: 'Beta', ...),
57+
gui.button(content: [gui.text(text: 'Reset')], ...),
58+
]
59+
)
60+
```
61+
62+
See `examples/wrap_panel.v` for a runnable demo.
63+
64+
### `canvas`
65+
66+
Places children with **no automatic layout**. Children are positioned
67+
using their own `x`/`y` fields. Use for free-form or absolutely
68+
positioned content.
69+
70+
```v ignore
71+
gui.canvas(
72+
width: 400
73+
height: 300
74+
sizing: gui.fixed_fixed
75+
content: [
76+
gui.row(x: 10, y: 20, ...),
77+
]
78+
)
79+
```
80+
81+
### `circle`
82+
83+
A column container rendered with a **circular boundary**. Children are
84+
arranged top to bottom inside the circle.
85+
86+
```v ignore
87+
gui.circle(
88+
width: 80
89+
height: 80
90+
sizing: gui.fixed_fixed
91+
content: [gui.text(text: 'OK')]
92+
)
93+
```
94+
95+
## Sizing
96+
97+
Every container has independent width and height sizing modes.
98+
99+
| Mode | Behavior |
100+
|----------|-----------------------------------------------------|
101+
| `.fit` | Shrinks to content size |
102+
| `.fill` | Expands/shrinks to fill remaining parent space |
103+
| `.fixed` | Explicit pixel size; `min` and `max` set to value |
104+
105+
Combine modes with the `Sizing` struct or use a preset constant:
106+
107+
```v ignore
108+
gui.fit_fit // shrink-wrap both axes (default)
109+
gui.fill_fit // fill width, shrink-wrap height
110+
gui.fill_fill // fill both axes
111+
gui.fixed_fixed // explicit width and height
112+
gui.fixed_fit // explicit width, shrink-wrap height
113+
// ... and four more combinations
114+
```
115+
116+
Pass explicit pixel sizes when using `.fixed` or as soft minimums for
117+
`.fit`/`.fill`:
118+
119+
```v ignore
120+
gui.column(
121+
sizing: gui.fixed_fit
122+
width: 300
123+
min_height: 100
124+
max_height: 400
125+
content: [...]
126+
)
127+
```
128+
129+
## Alignment
130+
131+
`h_align` controls alignment **along** the horizontal axis.
132+
`v_align` controls alignment **along** the vertical axis.
133+
134+
| `h_align` | Behavior |
135+
|------------|-----------------------------------------------|
136+
| `.start` | Culture-dependent start (default left) |
137+
| `.end` | Culture-dependent end (default right) |
138+
| `.center` | Center |
139+
| `.left` | Always left |
140+
| `.right` | Always right |
141+
142+
| `v_align` | Behavior |
143+
|------------|-----------------------------------------------|
144+
| `.top` | Default |
145+
| `.middle` | Center |
146+
| `.bottom` | Bottom |
147+
148+
In a `row`, `v_align` centers each child across the cross axis
149+
(vertically). In a `column`, `h_align` centers each child horizontally.
150+
151+
```v ignore
152+
// Row with items vertically centered
153+
gui.row(v_align: .middle, content: [...])
154+
155+
// Column with items horizontally centered
156+
gui.column(h_align: .center, content: [...])
157+
```
158+
159+
## Padding and Spacing
160+
161+
**Padding** insets the content area on all four sides (order: top,
162+
right, bottom, left — same as CSS).
163+
164+
```v ignore
165+
// Custom padding
166+
gui.column(padding: gui.padding(8, 16, 8, 16), content: [...])
167+
168+
// Uniform padding
169+
gui.column(padding: gui.pad_all(10), content: [...])
170+
171+
// Preset padding constants
172+
gui.column(padding: gui.padding_medium, content: [...])
173+
```
174+
175+
Preset padding constants: `padding_none`, `padding_x_small`,
176+
`padding_small`, `padding_medium`, `padding_large`.
177+
178+
**Spacing** is the gap inserted between consecutive children
179+
(`(n − 1) × spacing`).
180+
181+
```v ignore
182+
gui.column(spacing: 12, content: [...])
183+
```
184+
185+
## Scrolling
186+
187+
Set `id_scroll` to a non-zero value (unique per window) to enable
188+
scrolling. Content that overflows the container bounds is hidden until
189+
scrolled into view.
190+
191+
```v ignore
192+
gui.column(
193+
id_scroll: 1 // enables scrolling; unique per window
194+
height: 300
195+
sizing: gui.fixed_fit
196+
content: [/* many items */]
197+
)
198+
```
199+
200+
Restrict scroll direction with `scroll_mode`:
201+
202+
| `scroll_mode` | Behavior |
203+
|---------------------|----------------------------|
204+
| default | Both axes |
205+
| `.vertical_only` | Vertical scrolling only |
206+
| `.horizontal_only` | Horizontal scrolling only |
207+
208+
Customize scrollbars with `scrollbar_cfg_x` and `scrollbar_cfg_y`:
209+
210+
```v ignore
211+
gui.column(
212+
id_scroll: 1
213+
scrollbar_cfg_y: &gui.ScrollbarCfg{
214+
overflow: .on_hover // show scrollbar only when hovering
215+
}
216+
content: [...]
217+
)
218+
```
219+
220+
`ScrollbarCfg.overflow` options: `.auto` (default — show when content
221+
overflows), `.hidden` (never show), `.visible` (always show),
222+
`.on_hover` (show when mouse is over the scrollbar region).
223+
224+
## Floating
225+
226+
Floating containers draw **over** sibling content. Used for menus,
227+
dropdowns, and tooltips. The `float_anchor` point on the parent and the
228+
`float_tie_off` point on the float determine placement.
229+
230+
```v ignore
231+
gui.column(
232+
float: true
233+
float_anchor: .bottom_left // attach to parent's bottom-left
234+
float_tie_off: .top_left // align float's top-left to anchor
235+
float_offset_y: 4 // pixel offset after placement
236+
content: [...]
237+
)
238+
```
239+
240+
Nine `FloatAttach` positions are available (top/middle/bottom ×
241+
left/center/right): `.top_left`, `.top_center`, `.top_right`,
242+
`.middle_left`, `.middle_center`, `.middle_right`, `.bottom_left`,
243+
`.bottom_center`, `.bottom_right`.
244+
245+
## Group Box (Titled Border)
246+
247+
Embedding a `title` string places label text in the container's border,
248+
near the top-left corner — the classic "group box" pattern. Set
249+
`title_bg` to match the background so the border appears interrupted.
250+
251+
```v ignore
252+
gui.column(
253+
title: 'Connection'
254+
title_bg: gui.theme().color_background
255+
color_border: gui.theme().color_border
256+
size_border: 1
257+
padding: gui.padding_medium
258+
content: [...]
259+
)
260+
```
261+
262+
## Styling
263+
264+
| Field | Type | Effect |
265+
|--------------------|-------------|-------------------------------------------|
266+
| `color` | `Color` | Fill color (transparent by default) |
267+
| `color_border` | `Color` | Border color |
268+
| `size_border` | `f32` | Border thickness in pixels |
269+
| `radius` | `f32` | Corner rounding radius |
270+
| `shadow` | `&BoxShadow`| Drop shadow |
271+
| `gradient` | `&Gradient` | Fill gradient |
272+
| `border_gradient` | `&Gradient` | Border gradient |
273+
| `blur_radius` | `f32` | Background blur radius |
274+
| `opacity` | `f32` | 0.0 = transparent, 1.0 = opaque (default) |
275+
276+
## Clipping
277+
278+
Set `clip: true` to hide children that overflow the container bounds.
279+
Scroll containers enable clipping automatically.
280+
281+
```v ignore
282+
gui.row(clip: true, width: 200, content: [...])
283+
```
284+
285+
## Visibility and State
286+
287+
| Field | Effect |
288+
|-------------|----------------------------------------------------|
289+
| `invisible` | Returns an empty placeholder; excluded from layout |
290+
| `disabled` | Greys out the container and all descendants |
291+
| `opacity` | Renders container and children at reduced opacity |
292+
293+
## Focus and Keyboard Input
294+
295+
Set `id_focus` to a non-zero value to make the container focusable. The
296+
value sets the tab order. `focus_skip` keeps a container focusable by
297+
click but excludes it from tab navigation.
298+
299+
```v ignore
300+
gui.column(
301+
id_focus: 1
302+
on_keydown: fn (l &gui.Layout, mut e gui.Event, mut w gui.Window) {
303+
// handle keys
304+
}
305+
content: [...]
306+
)
307+
```
308+
309+
## Event Handlers
310+
311+
| Field | Signature |
312+
|----------------|----------------------------------------------------|
313+
| `on_click` | `fn (&Layout, mut Event, mut Window)` |
314+
| `on_any_click` | `fn (&Layout, mut Event, mut Window)` (all buttons)|
315+
| `on_char` | `fn (&Layout, mut Event, mut Window)` |
316+
| `on_keydown` | `fn (&Layout, mut Event, mut Window)` |
317+
| `on_mouse_move`| `fn (&Layout, mut Event, mut Window)` |
318+
| `on_mouse_up` | `fn (&Layout, mut Event, mut Window)` |
319+
| `on_hover` | `fn (mut Layout, mut Event, mut Window)` |
320+
| `on_scroll` | `fn (&Layout, mut Window)` |
321+
| `amend_layout` | `fn (mut Layout, mut Window)` (post-layout hook) |
322+
323+
`on_click` fires on left-click only. `on_any_click` fires on any mouse
324+
button. If `on_any_click` is set it takes precedence over `on_click`.
325+
326+
`amend_layout` runs after all sizes and positions are resolved. Use it
327+
for appearance changes based on final geometry (hover highlights, dynamic
328+
color). Do not change sizes inside this callback.
329+
330+
## Tooltips
331+
332+
Attach a tooltip to any container with the `tooltip` field:
333+
334+
```v ignore
335+
gui.row(
336+
tooltip: &gui.TooltipCfg{
337+
id: 'my-tooltip'
338+
content: [gui.text(text: 'More info')]
339+
}
340+
content: [...]
341+
)
342+
```
343+
344+
## Hero Transitions
345+
346+
Set `hero: true` to participate in animated hero transitions between
347+
layout states. Matching `id` values across frames are interpolated.
348+
349+
```v ignore
350+
gui.column(id: 'panel', hero: true, content: [...])
351+
```
352+
353+
## Related Files
354+
355+
- `view_container.v``ContainerCfg`, `column`, `row`, `wrap`,
356+
`canvas`, `circle`
357+
- `sizing.v``Sizing`, `SizingType`, preset constants
358+
- `alignment.v``Axis`, `HorizontalAlign`, `VerticalAlign`
359+
- `padding.v``Padding`, `padding()`, `pad_all()`, `pad_tblr()`
360+
- `layout_wrap.v` — wrap line-breaking algorithm
361+
- `layout_float.v``FloatAttach`, floating layout positioning
362+
- `view_scrollbar.v``ScrollbarCfg`, `ScrollMode`
363+
- `docs/LAYOUT_ALGORITHM.md` — full layout pipeline reference
364+
365+
## Examples
366+
367+
- `examples/wrap_panel.v` — wrap container with mixed widget types
368+
- `examples/column_scroll.v` — scrollable column with 10,000-item list

0 commit comments

Comments
 (0)