Skip to content

Controls

Two controls on this page stand on their own rather than inside a boxed list. A single-line entry takes typed text, and a drop down picks one value out of a list. Both belong to GTK. They are Gtk.Entry and Gtk.DropDown, libadwaita ships neither, and the Adwaita stylesheet gives them their GNOME look.

Each has a boxed-list twin, Adw.EntryRow and Adw.ComboRow, documented under Boxed Lists. Which one you want is a question about the surrounding layout rather than about the control. A preferences page uses the rows, a toolbar or a dialog uses these.

Each block below is a stack of windows. The first one RUNS the widget: the preview is the live browser port, so it follows the light and dark toggle the way a real window would, and the HTML tab beside it holds the very markup that paints it — one import '@gjsify/adwaita-web' registers every element in it. Then comes one window per kind of implementation — Native TypeScript, the @girs program that runs unchanged on GJS, Node, Bun and Deno, with the matching Blueprint declaration beside it, UI frameworks, the same widget written in Solid, Vue and React through @gjsify/gtk-host — or, where the widget is built imperatively rather than declared, the recorded reason it has no such snippet — and NativeScript, the port that runs on a phone. That last window splits the way the first one does. An XML template holds the tree, and the TypeScript beside it is the loader.

GTK 4 is the reference here, typed by @girs/gtk-4.0. The browser port spells both controls the way the GIR does — <gtk-entry> and <gtk-drop-down>, the same tags @gjsify/gtk-host uses — because a widget is named after the library that owns it and the Adwaita part of these two is the stylesheet. The NativeScript port names them by the same rule, GtkEntry and GtkDropDown. Where a port behaves differently, its own window says so.

A single-line text field. Give it a placeholder to say what belongs in it while it is empty, a value for the text itself, and disabled when it should be read-only. For a field that belongs in a preferences group, use Entry Row instead. It is the same control in row presentation, and the label doubles as the row title.

Gtk.Entry
HTML
<div style="display:flex;flex-wrap:wrap;gap:12px;justify-content:center;align-items:center;">
<gtk-entry placeholder="Search files…" style="min-width: 280px;"></gtk-entry>
<!-- `<gtk-entry>` has no `editable`: the browser spelling of a field you may
read but not change is `disabled`, which also greys it. -->
<gtk-entry value="notes.md" disabled></gtk-entry>
</div>
<gtk-entry> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
value notes.md
placeholder Search files…
type not used
disabled set
maxlength not used
Native TypeScript
TypeScript
import Gtk from 'gi://Gtk?version=4.0';
const entry = new Gtk.Entry({
placeholderText: 'Search files…',
widthRequest: 280,
halign: Gtk.Align.CENTER,
});
entry.text = 'notes.md';
entry.editable = false; // read but do not change
Code
using Gtk 4.0;
Gtk.Entry {
placeholder-text: _("Search files…");
width-request: 280;
halign: center;
}
UI frameworks
TypeScript
// mount(() => <GtkEntry />, container) — from '@gjsify/gtk-host/solid'
const GtkEntry = () => (
<gtk-entry
placeholderText="Search files…"
widthRequest={280}
/>
);
Vue
<!-- GtkEntry.vue — mount(GtkEntry, container) from '@gjsify/gtk-host/vue' -->
<template>
<gtk-entry
placeholder-text="Search files…"
:width-request="280"
/>
</template>
TypeScript
// createRoot(container).render(<GtkEntry />) — from '@gjsify/gtk-host/react'
const GtkEntry = () => (
<gtk-entry
placeholderText="Search files…"
widthRequest={280}
/>
);
NativeScript
TypeScript
import { GtkEntry } from '@gjsify/adwaita-nativescript';
const entry = new GtkEntry();
entry.placeholderText = 'Search files…';
entry.text = 'notes.md';
entry.editable = false;
XML
<adw:GtkEntry
xmlns="http://schemas.nativescript.org/tns.xsd"
xmlns:adw="~/adwaita"
placeholderText="Search files…"
/>

Pick one value from a list. The button shows the current selection and the list opens in a popover; selected is the zero-based index of the chosen option, and enable-search adds a filter field for a long list. For the same choice presented as a boxed-list row, use Combo Row.

Gtk.DropDown
HTML
<!-- `options` is a JSON array of strings, or of { label, value } objects. -->
<gtk-drop-down options='["Automatic","Always","Never","When busy"]' selected="0"></gtk-drop-down>
<gtk-drop-down> observes these in the browser port. Changing one re-renders the element. The second column is what the preview one tab to the left writes, read across every copy of the element in it. The other windows name their own properties.
Attribute In this preview
options ["Automatic","Always","Never","When busy"]
items not used
selected 0
enable-search not used
disabled not used
Native TypeScript
TypeScript
import Gtk from 'gi://Gtk?version=4.0';
const dropDown = Gtk.DropDown.new_from_strings(['Automatic', 'Always', 'Never', 'When busy']);
// Without an expression the search field has nothing to match against.
dropDown.expression = Gtk.PropertyExpression.new(Gtk.StringObject.$gtype, null, 'string');
dropDown.selected = 0;
dropDown.enableSearch = false;
dropDown.halign = Gtk.Align.CENTER;
Code
using Gtk 4.0;
Gtk.DropDown {
selected: 0;
halign: center;
model: Gtk.StringList {
strings [_("Automatic"), _("Always"), _("Never"), _("When busy")]
};
}
UI frameworks

No Solid, Vue or React snippet for Gtk.DropDown: its options are a Gtk.StringList model, built imperatively.

Every snippet in this gallery was compiled and run before it was written down. Where a widget is built imperatively rather than declared — a Gio.MenuModel, a dialog opened with present(), a container with no child policy — there is nothing for the three dialects to differ about, so the reason is recorded here rather than the tab being quietly absent.

NativeScript
TypeScript
import { GtkDropDown } from '@gjsify/adwaita-nativescript';
const dropDown = new GtkDropDown();
dropDown.options = ['Automatic', 'Always', 'Never', 'When busy'].map((label) => ({ value: label, label }));
dropDown.selected = 0;
// The list opens as the platform action sheet, which has no search field —
// so `enableSearch` has no NativeScript equivalent.

No NativeScript XML template for Gtk.DropDown: GtkDropDown.options is an array of choices; an XML attribute is a string.

NativeScript’s Builder reaches a widget through an attribute, which is always a string, and through a child, which lands only where the widget declares a slot for it. A property that is an array of options or a reference to another view fits through neither, so the TypeScript beside this window is where that widget is built.

  • Buttons: the plain button, its style classes and the menu button.
  • Boxed Lists: the row twins of both controls, entry row, password entry row, combo row and spin row.
  • Adwaita Storybook: the same widgets in a live component browser you can poke at.