Core widgets
Widgets are the controls users interact with — buttons they click, fields they type into, checkboxes they toggle. This lesson introduces the controls you will use in almost every application.
You will work with the app.rb skeleton from the previous lesson, adding one widget at a time and running the app after each addition. By the end of the lesson the skeleton will have grown into a working registration form.
The skeleton from lesson 2.1 should still be open. If you need to recreate it, go back and type it in before continuing here.
A note on Ruby-style method names
wxRuby3 is a Ruby binding for a C++ library, and many methods exist in both a C++ style and a Ruby style. Throughout this series we use the Ruby style consistently:
| C++ style | Ruby style |
|---|---|
get_value |
value |
set_value('x') |
value = 'x' |
get_label |
label |
set_label('x') |
label = 'x' |
get_id |
id |
is_checked |
checked? |
When you read community code, wxWidgets C++ documentation, or Python wxPython examples, you will see the get_/set_ style — and it works fine in wxRuby3 too. But in this series we write Ruby, so we use the Ruby style. The mapping above is the same pattern for every widget you will encounter.
The constructor pattern
Every widget follows the same constructor:
|
|
The first argument is always the parent — the panel or container the widget belongs to. Everything else is a keyword option: label:, value:, style:, size:, and so on. Most have sensible defaults and can be omitted.
A note on positioning in this lesson
Proper layout in wxRuby3 is handled by sizers — that is the subject of the next lesson. For now, we use absolute positioning with pos: [x, y] to keep widgets visible and separated as we add them. This is not how real apps are built, and lesson 2.3 will replace it entirely. Think of pos: here as temporary scaffolding: it keeps things readable while we focus on learning what each widget does.
Step 1 — StaticText
Wx::StaticText displays a non-interactive label. Add the following to build_ui, replacing the existing empty method:
|
|
Run the app. The label “Name:” appears at position [20, 20] — 20 pixels from the left and top edges of the panel.
To change a label’s text at runtime:
|
|
Step 2 — TextCtrl
Wx::TextCtrl is the text input field. Update build_ui to add a name field next to the label:
|
|
Run it. The label and field sit on the same row, the field accepting input immediately. The size: [200, -1] sets the width to 200 pixels; -1 tells wxRuby3 to use the default height for the platform. Read the value in a method with:
|
|
Add a second row for email below the first:
|
|
Run it. Two clean rows, label and field side by side.
TextCtrl has several useful style variants. You will use these in later lessons:
|
|
Step 3 — CheckBox
Wx::CheckBox is a labelled boolean toggle. Add one below the email field:
|
|
Run it. The checkbox appears, checked. Read its state with:
|
|
Now wire up a handler in bind_events so something happens when the user toggles it:
|
|
Run it. Toggle the checkbox and watch the status bar update. This is the first time we are handling a widget event — notice the pattern: evt_checkbox in bind_events takes the widget’s id, and the block receives an event object. event.checked? is the Ruby-style equivalent of the C++ event.IsChecked() you will see in wxWidgets documentation.
Step 4 — RadioButton
Radio buttons present a set of mutually exclusive options. wxRuby3 determines group membership by creation order: all buttons created after a Wx::RB_GROUP button, and before the next Wx::RB_GROUP button, belong to the same group. This means you can have multiple independent groups on the same panel — the Wx::RB_GROUP flag acts as the boundary marker between them.
Add two groups — one for size, one for colour. Store each group as an array of buttons:
|
|
Run it. The two groups operate completely independently — selecting “Large” has no effect on the colour selection, and vice versa. wxRuby3 knows which group each button belongs to because of the Wx::RB_GROUP flag on the first button of each group.
Now wire up handlers for both groups. Storing buttons in arrays pays off here — one registration loop per group, and the handler uses find(&:value) to locate the selected button and reads its label directly, so you never have to keep button names synchronised with a separate list of strings:
|
|
|
|
Run it. The status bar shows both selections updating as you click either group. find(&:value) returns the first button whose value is true — the selected one — and .label gives you its text without any if/elsif chain.
Step 5 — Choice
Wx::Choice is a drop-down list that shows only the selected item. Add a country selector:
|
|
Run it. The drop-down shows “Australia” and expands when clicked. Read the selection:
|
|
Add and remove items at runtime if needed:
|
|
Wire up a handler:
|
|
Run it. The status bar shows the selected country as you change it.
Wx::ComboBox is a related control that adds a text field, allowing the user to type a value not in the list. Use Wx::Choice when the options are fixed, Wx::ComboBox when the user might provide their own value.
Step 6 — Slider
Wx::Slider lets the user select a numeric value within a range by dragging a handle. Add an age slider with a label that updates as it moves:
|
|
Wire up the slider event in bind_events:
|
|
Run it. Drag the slider and the label updates in real time. event.int returns the slider’s current integer value — this is the Ruby equivalent of the C++ event.GetInt().
Step 7 — Button and submit
Add a submit button and wire it to a handler that reads all the values and shows them in a message box:
|
|
|
|
Run it. Fill in the form and click Submit — a native message dialog shows everything you entered.
The finished lesson app
Your complete app.rb at the end of this lesson:
|
|
The widgets are laid out with absolute pos: values — workable, but inflexible. The form does not resize gracefully and the positioning is manual arithmetic. Sizers replace all of that in the next lesson, and you will apply them to this exact app to see the difference.
Previous: Frames and panels | Next: Layout with sizers