multiple_windows/
multiple_windows.rs

1// Copyright 2025 the Xilem Authors
2// SPDX-License-Identifier: Apache-2.0
3
4//! An example of an app with multiple windows.
5
6// On Windows platform, don't show a console when opening the app.
7#![windows_subsystem = "windows"]
8
9use std::collections::{BTreeMap, HashMap};
10
11use winit::error::EventLoopError;
12use xilem::view::{flex_col, label, text_button, text_input};
13use xilem::{AppState, EventLoop, EventLoopBuilder, WindowId, WindowView, Xilem, window};
14
15struct State {
16    new_counter_name: String,
17    counters: HashMap<WindowId, Counter>,
18    running: bool,
19    main_window_id: WindowId,
20}
21
22struct Counter {
23    name: String,
24    value: isize,
25}
26
27impl AppState for State {
28    fn keep_running(&self) -> bool {
29        self.running
30    }
31}
32
33fn app_logic(state: &mut State) -> impl Iterator<Item = WindowView<State>> + use<> {
34    std::iter::once(
35        window(
36            state.main_window_id,
37            "Multiple windows",
38            flex_col((
39                label(format!(
40                    "{:#?}",
41                    state
42                        .counters
43                        .values()
44                        .map(|counter| (&counter.name, counter.value))
45                        .collect::<BTreeMap<_, _>>()
46                )),
47                text_input(
48                    state.new_counter_name.clone(),
49                    |state: &mut State, new_name| {
50                        state.new_counter_name = new_name;
51                    },
52                ),
53                text_button("Add".to_string(), |state: &mut State| {
54                    if state
55                        .counters
56                        .values()
57                        .any(|counter| counter.name == state.new_counter_name)
58                    {
59                        // TODO: show error if name already exists
60                        return;
61                    }
62                    let name = std::mem::take(&mut state.new_counter_name);
63                    state
64                        .counters
65                        .insert(WindowId::next(), Counter { name, value: 0 });
66                }),
67            )),
68        )
69        .with_options(|o| o.on_close(|state: &mut State| state.running = false)),
70    )
71    .chain(
72        state
73            .counters
74            .iter()
75            .map(|(window_id, Counter { name, value })| {
76                let window_id = *window_id;
77                window(
78                    window_id,
79                    name,
80                    flex_col((
81                        label(format!("count: {value}")),
82                        text_button("+".to_string(), move |state: &mut State| {
83                            state.counters.get_mut(&window_id).unwrap().value += 1;
84                        }),
85                        text_button("-".to_string(), move |state: &mut State| {
86                            state.counters.get_mut(&window_id).unwrap().value -= 1;
87                        }),
88                    )),
89                )
90                .with_options(|o| {
91                    o.on_close(move |state: &mut State| {
92                        state.counters.remove(&window_id);
93                    })
94                })
95            }),
96    )
97    .collect::<Vec<_>>()
98    .into_iter()
99}
100
101fn run(event_loop: EventLoopBuilder) -> Result<(), EventLoopError> {
102    let data = State {
103        new_counter_name: String::new(),
104        counters: HashMap::new(),
105        running: true,
106        main_window_id: WindowId::next(),
107    };
108    let app = Xilem::new(data, app_logic);
109    app.run_in(event_loop)
110}
111
112fn main() -> Result<(), EventLoopError> {
113    run(EventLoop::with_user_event())
114}