Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Search Entry City Lookup and Suggestion Popover

The search entry in the Weather App allows users to search for cities dynamically.

Instead of requiring users to type the full city name correctly, the application provides live suggestions using the Open-Meteo geocoding API. This improves usability and reduces input errors. In the sidebar, the search entry is placed together with the add city button. This means the top interaction area supports both:

  • Searching for cities
  • Adding a city explicitly

This keeps the interface compact and easy to understand.

Creating the Search Entry

The Weather App creates the search field like this:

#![allow(unused)]
fn main() {
let search = gtk::SearchEntry::new();
search.set_hexpand(true);
search.set_placeholder_text(Some("Search city…"));
}

SearchEntry is used instead of a normal Entry because:

  • It is visually optimized for search tasks
  • It includes built-in styling
  • It integrates well with GNOME design patterns

The field expands horizontally to fill available space inside the sidebar.

Adding the Add City Button

Next to the search field, the Weather App creates an add button:

#![allow(unused)]
fn main() {
let add_city_btn = gtk::Button::from_icon_name("list-add-symbolic");
}

This button gives users a direct way to add a city.

Using an icon button keeps the interface minimal and matches GNOME design conventions. The plus symbol clearly communicates the purpose of the action.

Combining Search Entry and Add Button in One Row

The search entry and add button are placed together inside a horizontal layout:

#![allow(unused)]
fn main() {
let search_row = gtk::Box::new(gtk::Orientation::Horizontal, 8);

search_row.append(&search);
search_row.append(&add_city_btn);
}

This creates a single interaction row at the top of the sidebar.

The order of the append() calls is important:

  • search_row.append(&search); places the search field first
  • search_row.append(&add_city_btn); places the add button next to it

So the layout appears like this:

[ SearchEntry | Add Button ]

This arrangement improves usability because:

  • Search and add actions are closely related
  • Users can immediately see where to type
  • Users can also add a city directly from the same row

The search field expands, while the add button remains compact.

Suggestion Popover Structure

When the user types, suggestions appear in a popover.

The structure is:

#![allow(unused)]
fn main() {
let suggestion_list = gtk::ListBox::new();

let suggestion_popover = gtk::Popover::new();
suggestion_popover.set_parent(&search);
suggestion_popover.set_child(Some(&suggestion_list));
}

This creates:

  • A floating popover
  • Attached directly to the search field
  • Containing a list of suggestion rows

This avoids navigating away from the sidebar.

The suggestion UI remains lightweight and contextual.

Reacting to User Input

The Weather App listens to changes in the search field:

#![allow(unused)]
fn main() {
search.connect_search_changed(move |s| {
    let q = s.text().to_string();
});
}

Whenever the text changes:

  • The current query is captured
  • If the text is too short, suggestions are hidden
  • Otherwise, a background request is triggered

This prevents unnecessary API calls.

Fetching Suggestions (Background Thread)

To avoid freezing the UI, the API call runs in a separate thread:

#![allow(unused)]
fn main() {
thread::spawn(move || {
    let res = fetch_geocoding_suggestions(&q_clone, 5);
});
}

This ensures:

  • The UI remains responsive
  • The search field does not lag
  • Users can continue typing

GTK’s main thread is never blocked.

Updating the UI Safely

After receiving suggestions, the UI is updated using a timeout polling mechanism:

#![allow(unused)]
fn main() {
glib::timeout_add_local(Duration::from_millis(80), move || {
    match rx.try_recv() {
        ...
    }
});
}

This allows:

  • Safe UI updates from the main thread
  • Controlled checking of background results
  • Smooth suggestion appearance

This design respects GTK’s single-threaded UI model.

Displaying Suggestions

When results are returned, the list is rebuilt:

#![allow(unused)]
fn main() {
clear_listbox(&suggestion_list);

for item in items {
    let row = ActionRow::new();
    row.set_title(&item.name);
    suggestion_list.append(&row);
}
}

Each suggestion:

  • Is displayed as an ActionRow
  • Can be clicked
  • Updates the search field
  • Triggers weather fetch

This keeps the interaction intuitive.

Handling No Results

If no city is found:

#![allow(unused)]
fn main() {
row.set_title("No city found");
row.set_subtitle("Try another spelling");
}

This avoids silent failure.

The user receives immediate feedback.

Why This Design Is Important

This search system demonstrates:

  • Real-time UI interaction
  • Safe multi-threading in GTK
  • Controlled UI updates
  • Clean separation between network logic and UI logic

From an onboarding perspective, it teaches:

  • Event-driven programming
  • Background processing in Rust
  • GTK-safe UI updates
  • User-friendly feedback handling

Architectural Flow

Search workflow in the Weather App:

  1. User types into SearchEntry
  2. search_changed signal triggers
  3. Background thread calls geocoding API
  4. Main thread updates suggestion popover
  5. User selects a suggestion
  6. Weather fetch begins

This flow is modular and easy to follow.