Adding Cities with a Modal Dialog and Validation
In the Weather App, users can add a new city using the “+” button in the sidebar. When the button is clicked, a small popup window appears asking the user to enter a city name.This popup is called a modal dialog.
A modal dialog temporarily blocks the main window until the user finishes the task. The user must either add the city or cancel the action before returning to the main interface. This design keeps the interaction simple and prevents accidental actions.
Creating the Modal Dialog
The add-city window is created using a secondary ApplicationWindow configured as a modal dialog.
#![allow(unused)] fn main() { let dialog = AdwWindow::builder() .application(&app) .title("Add City") .transient_for(&parent) .modal(true) .default_width(360) .default_height(160) .build(); }
Important parts:
transient_for(&parent)connects the dialog to the main window.modal(true)prevents interaction with the main window.- The dialog behaves like a temporary input window.
This ensures the user focuses only on entering the city name.
Adding the Input Field
Inside the dialog, a vertical layout is created.
#![allow(unused)] fn main() { let content = gtk::Box::new(gtk::Orientation::Vertical, 12); let entry = gtk::Entry::new(); entry.set_placeholder_text(Some("Enter city name…")); content.append(&entry); }
This creates a text input field where the user can type the city name. The placeholder text helps the user understand what to enter.
Adding Action Buttons
Two buttons are added to the dialog:
#![allow(unused)] fn main() { let cancel_btn = gtk::Button::with_label("Cancel"); let add_btn = gtk::Button::with_label("Add"); add_btn.add_css_class("suggested-action"); }
The Cancel button closes the dialog without saving anything.
The Add button performs the main action.
The CSS class suggested-action highlights the Add button so the user can easily recognize the primary action.
Reading the User Input
When the user clicks Add, the application reads the text entered in the input field.
#![allow(unused)] fn main() { let name = entry.text().trim().to_string(); }
The .trim() function removes extra spaces before and after the text.
For example:
" Berlin "
becomes:
"Berlin"
This keeps the stored data clean.
Validation: Preventing Empty Input
Before saving the city, the application checks whether the input is empty.
#![allow(unused)] fn main() { if name.is_empty() { let msg = MessageDialog::builder() .transient_for(&parent) .modal(true) .heading("Empty city name") .body("Please enter a valid city name.") .build(); msg.add_response("ok", "OK"); msg.present(); return; } }
If the user tries to submit an empty value, a message dialog appears. The dialog informs the user that a valid city name must be entered. This prevents invalid data from entering the application.
Validation: Preventing Duplicate Cities
The application also checks whether the city already exists.
#![allow(unused)] fn main() { if cities.borrow().iter().any(|c| c.eq_ignore_ascii_case(&name)) { let msg = MessageDialog::builder() .transient_for(&parent) .modal(true) .heading("City already exists") .body("This city is already in your list.") .build(); msg.add_response("ok", "OK"); msg.present(); return; } }
Here the function eq_ignore_ascii_case compares city names without considering uppercase or lowercase letters.
This means:
"Berlin""berlin"
are treated as the same city. This keeps the city list clean and prevents duplicates.
Saving the City
Only after passing the validation checks is the city stored.
#![allow(unused)] fn main() { cities.borrow_mut().push(name); save_cities_to_csv(&cities.borrow()); rebuild_list(); dialog.close(); }
These steps perform the following actions:
- Add the city to the in-memory list
- Save the list to the CSV file
- Refresh the sidebar city list
- Close the dialog
This ensures the UI and stored data remain synchronized.
Why This Design Is Important
Using a modal dialog with validation helps maintain a clean and reliable application.
It ensures that:
- users focus on one task at a time
- invalid input is prevented
- duplicate data is avoided
- the application state remains consistent
This pattern is commonly used in many desktop applications.
What Freshers Learn from This
This implementation helps beginners understand several important concepts:
- creating secondary windows in GTK
- connecting dialogs to a parent window
- reading user input from widgets
- validating input before saving data
- updating the UI after user actions
In the Weather App, the modal dialog collects the city name, validates it, updates the application state, and then closes. This demonstrates how user interaction, validation, data storage, and UI updates work together in a Rust + GTK application.