Weather Panel and Forecast List
The Weather Panel is the right side of the Weather App. It displays the weather information for the city selected in the sidebar.
This panel has two responsibilities:
- Show the current weather conditions
- Show the 7-day forecast list
However, when the application first starts, no city is selected, so no weather data should be visible.
To handle this situation cleanly, the Weather App uses a state-based UI layout.
Managing Panel States with GtkStack
The weather panel uses a GtkStack to switch between two states.
#![allow(unused)] fn main() { let content_stack = gtk::Stack::new(); content_stack.set_hexpand(true); content_stack.set_vexpand(true); }
The stack contains two pages:
#![allow(unused)] fn main() { content_stack.add_named(&right_empty, Some("empty")); content_stack.add_named(&content, Some("weather")); content_stack.set_visible_child_name("empty"); }
These represent two possible UI states.
| State | Meaning |
|---|---|
empty | No city has been selected |
weather | Weather data is available |
When the application starts, the empty page is shown. After weather data is successfully fetched, the UI switches to the weather page.
Building the Weather Layout
Inside the weather page, the interface is built using a vertical layout container.
#![allow(unused)] fn main() { let content = gtk::Box::new(gtk::Orientation::Vertical, 16); set_margins(&content, 18); }
This container organizes the weather widgets in a vertical order.
The layout contains:
- Weather icon
- City title
- Weather description
- Current temperature
- Forecast heading
- Forecast list
Each component has a specific role in presenting the weather information.
Displaying the City Name
The selected city name is shown using a label.
#![allow(unused)] fn main() { let city_title = gtk::Label::new(Some("")); city_title.add_css_class("title-1"); city_title.set_halign(gtk::Align::Center); }
When weather data is applied, the label is updated dynamically:
#![allow(unused)] fn main() { city_title.set_text(&format!("Weather in {name}")); }
This demonstrates how UI elements react to updated application data.
Showing Current Weather
The current weather is displayed using a combination of an icon, a description label, and a temperature label.
For example, the temperature label is created as follows:
#![allow(unused)] fn main() { let now_temp = gtk::Label::new(Some("")); now_temp.add_css_class("title-4"); now_temp.set_halign(gtk::Align::Center); }
When the weather fetch completes, the UI updates:
#![allow(unused)] fn main() { now_temp.set_text(&format!("Now: {:.1} °C", now)); }
The weather condition code returned by the API is converted into a text description and icon before being displayed.
This ensures the UI shows user-friendly information rather than raw numeric codes.
Forecast List
Below the current temperature, the Weather App displays the 7-day forecast.
The forecast is implemented using a ListBox.
#![allow(unused)] fn main() { let forecast_list = gtk::ListBox::new(); forecast_list.add_css_class("boxed-list"); forecast_list.set_selection_mode(gtk::SelectionMode::None); }
The ListBox widget is suitable for this purpose because it allows multiple rows stacked vertically with GNOME styling.
Rows are not interactive, so selection mode is disabled.
Rebuilding the Forecast List
Each time new weather data is fetched, the forecast list is rebuilt.
First, existing rows are removed:
#![allow(unused)] fn main() { clear_listbox(&forecast_list); }
This step is important because it prevents previous forecast entries from remaining visible.
After clearing the list, new rows are created for each forecast day.
A simplified version of the row creation looks like this:
#![allow(unused)] fn main() { let row = ActionRow::new(); row.set_title(&date); row.set_subtitle(&format!( "{} • Max {:.1}°C Min {:.1}°C", weather_text, max, min )); forecast_list.append(&row); }
Each row represents one forecast day and displays:
- the forecast date
- the weather description
- the maximum temperature
- the minimum temperature
An icon corresponding to the weather condition is also added to the row.
Where the Forecast Data Comes From
The forecast list receives its data from the weather fetch function.
The function returns structured forecast information in the form:
#![allow(unused)] fn main() { Vec<(String, f64, f64, i32)> }
Each tuple contains:
- the forecast date
- the maximum temperature
- the minimum temperature
- the weather code
The UI layer simply loops through this data and creates a row for each day.
This design ensures that the UI only displays data and does not calculate it.
Applying Weather Data to the Panel
When the background fetch completes successfully, the weather panel is updated.
First, the panel switches from the empty state to the weather state.
#![allow(unused)] fn main() { content_stack.set_visible_child_name("weather"); }
Then the labels and forecast list are updated using the returned data.
This dynamic update ensures that the UI always reflects the latest weather information.
Handling Errors in the Panel
If an error occurs during the weather request, the panel displays an informative message.
For example:
#![allow(unused)] fn main() { now_temp.set_text("No city found. Try another spelling."); forecast_label.set_visible(false); forecast_list.set_visible(false); }
This ensures that:
- the user sees a clear error message
- outdated forecast rows are hidden
- the interface remains consistent
Handling errors visually is an important part of creating a user-friendly application.
Expected Behavior
When the Weather App runs, the user should observe the following behavior.
At startup:
- the weather panel shows an empty state
After selecting a city:
- the city name appears
- the current temperature appears
- the weather description and icon appear
- the 7-day forecast list appears
If an invalid city is entered:
- a message is shown instead of weather data
This confirms that the panel correctly reacts to changes in application state.