Layout

A widget isn't visible until you place it with a geometry manager. ScriptWeaver gives you the three Tk managers — pack, grid, place — plus two higher-level helpers, Flex and Grid.

Don't mix managers in one parent. Use pack or grid for the children of a given container, not both — mixing them inside the same parent can hang the layout. Different containers may use different managers.

Each widget exposes its manager as a proxy: w.pack, w.grid, w.place. You configure placement with .configure({…}) and undo it with .forget().

pack — stacking

pack stacks widgets against a side of their container. Great for toolbars, button rows, and simple top-to-bottom forms.

const bar = app.TFrame();
bar.pack.configure({ side: 'top', fill: 'x' });

bar.TButton({ text: 'Open' }).pack.configure({ side: 'left' });
bar.TButton({ text: 'Save' }).pack.configure({ side: 'left', padx: 4 });

Common options: side ('top' | 'bottom' | 'left' | 'right'), fill ('x' | 'y' | 'both'), expand (claim leftover space), padx / pady (outer spacing), and ipadx / ipady (inner padding).

grid — rows and columns

grid places widgets in a table. It's the workhorse for forms and anything aligned in columns.

const form = app.TFrame({ padding: 10 });
form.pack.configure({ fill: 'both', expand: true });

form.TLabel({ text: 'Name' }).grid.configure({ row: 0, column: 0, sticky: 'w' });
form.TEntry().grid.configure({ row: 0, column: 1, sticky: 'ew' });

form.TLabel({ text: 'Email' }).grid.configure({ row: 1, column: 0, sticky: 'w' });
form.TEntry().grid.configure({ row: 1, column: 1, sticky: 'ew' });

// let column 1 (the inputs) absorb extra width
form.grid.columnConfigure(1, { weight: 1 });

Common cell options: row, column, sticky (any of n / s / e / w — the edges the widget clings to), columnspan / rowspan, padx / pady. Make a row or column stretch with parent.grid.rowConfigure(i, { weight: 1 }) or columnConfigure(i, { weight: 1 }).

place — absolute / relative

place positions a widget by exact or proportional coordinates. Use it sparingly — for overlays, or when you need pixel control.

badge.place.configure({ relx: 1, rely: 0, anchor: 'ne', x: -8, y: 8 });

Options include x / y (pixels), relx / rely (0–1, a fraction of the parent), relwidth / relheight, and anchor.

Flex and Grid — higher-level helpers

For responsive layouts, two helpers wrap grid with a friendlier, flexbox-like API. Create them like any container, passing a children array.

Flex arranges children in a row or column:

const row = app.Flex({
  direction: 'row', // or 'column'
  gap: 8,
  align: 'center',
  children: [
    app.TButton({ text: 'Cancel' }),
    app.TButton({ text: 'OK', style: 'Accent.TButton' }),
  ],
});
row.pack.configure({ fill: 'x' });

Give a child grow: 1 to make it expand into spare space; set align per child to override the container default.

Grid lays out a track-based grid — pass columns as a count (equal widths) or a list of weights, and children as a flat list or as rows of arrays:

const pad = app.Grid({
  columns: 3,
  gap: 6,
  children: [
    [app.TButton({ text: '7' }), app.TButton({ text: '8' }), app.TButton({ text: '9' })],
    [app.TButton({ text: '4' }), app.TButton({ text: '5' }), app.TButton({ text: '6' })],
  ],
});
pad.pack.configure({ expand: true, fill: 'both' });

Editing a Flex or Grid at runtime

Both helpers are editable after construction — useful for lists that grow and shrink, or for any UI that reorders itself:

row.add(widget); // append
row.insertAt(0, widget); // insert at an index (an array inserts in order)
row.remove(widget); // unmap and drop it (the widget is not destroyed)
row.move(widget, 2); // reorder to a new index
row.clear(); // remove every child

row.childList(); // ordered snapshot (a copy) of the laid-out children
row.indexOf(widget); // its position, or -1

The layout hints (grow, align, row, col, colSpan, rowSpan, sticky) are plain properties on each child, so changing one does not reflow on its own. Set it, then call relayout():

okButton.grow = 1;
row.relayout(); // OK now expands into the spare space

A removed child is only unmapped, not destroyed, so you can re-insertAt() it later. Call widget.destroy() yourself if you want it gone for good. Every edit method returns the frame, so calls chain: row.remove(a).insertAt(0, b).

Next