Skip to content

Commit d2cc7b7

Browse files
committed
feat: window focus & rename title
1 parent 5160867 commit d2cc7b7

4 files changed

Lines changed: 240 additions & 54 deletions

File tree

examples/multi_window.rs

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Demonstrate opening additional windows from a pure Dioxus app.
22
33
use dioxus::prelude::*;
4-
use dioxus_native::DioxusWindowHandle;
4+
use dioxus_native::{DioxusWindowHandle, DioxusWindowInfo, DioxusWindowOptions};
55

66
fn main() {
77
dioxus_native::launch(app);
@@ -12,27 +12,91 @@ fn app() -> Element {
1212
let mut counter = use_signal(|| 1u32);
1313
let open_simple = window_handle.clone();
1414
let open_with_props = window_handle.clone();
15+
let refresh_handle = window_handle.clone();
16+
let base_focus_handle = window_handle.clone();
17+
let base_rename_handle = window_handle.clone();
18+
let mut known_windows: Signal<Vec<DioxusWindowInfo>> =
19+
use_signal(|| window_handle.list_windows());
20+
let known_windows_signal = known_windows.clone();
21+
let refresh_handle_for_list = refresh_handle.clone();
22+
let rename_counter = use_signal(|| 1u32);
23+
let window_rows = {
24+
let windows_snapshot = known_windows();
25+
windows_snapshot
26+
.into_iter()
27+
.map(|info| {
28+
let focus_handle = base_focus_handle.clone();
29+
let rename_handle = base_rename_handle.clone();
30+
let mut rename_counter_signal = rename_counter.clone();
31+
let mut update_list_signal = known_windows_signal.clone();
32+
let update_source = refresh_handle_for_list.clone();
33+
let id = info.id;
34+
let title = info.title;
35+
rsx! {
36+
li {
37+
"{title} (ID: {id:?})"
38+
button {
39+
onclick: move |_| focus_handle.focus_window(id),
40+
"Focus"
41+
}
42+
button {
43+
onclick: move |_| {
44+
let idx = rename_counter_signal();
45+
rename_handle.set_window_title(id, format!("Renamed {idx}"));
46+
rename_counter_signal += 1;
47+
update_list_signal.set(update_source.list_windows());
48+
},
49+
"Rename"
50+
}
51+
}
52+
}
53+
})
54+
.collect::<Vec<_>>()
55+
};
1556

1657
rsx! {
1758
main {
1859
style { {PRIMARY_STYLES} }
1960
h1 { "Blitz multi-window" }
2061
p { "Click the button to open another RSX window." }
21-
button {
22-
onclick: move |_| open_simple.open_window(secondary_window),
23-
"Open secondary window"
24-
}
25-
button {
26-
onclick: move |_| {
27-
let idx = counter();
28-
open_with_props.open_window_with_props(
29-
message_window,
30-
MessageWindowProps { message: format!("Window #{idx}") },
31-
);
32-
counter += 1;
33-
},
34-
"Open window with props"
62+
div {
63+
button {
64+
onclick: move |_| {
65+
open_simple.open_window_with_options(
66+
secondary_window,
67+
DioxusWindowOptions {
68+
title: Some("Secondary window".into()),
69+
..Default::default()
70+
},
71+
);
72+
known_windows.set(open_simple.list_windows());
73+
},
74+
"Open secondary window"
75+
}
76+
button {
77+
onclick: move |_| {
78+
let idx = counter();
79+
open_with_props.open_window_with_props_and_options(
80+
message_window,
81+
MessageWindowProps {
82+
message: format!("Window #{idx}"),
83+
},
84+
DioxusWindowOptions {
85+
title: Some(format!("Window #{idx}")),
86+
..Default::default()
87+
},
88+
);
89+
counter += 1;
90+
known_windows.set(open_with_props.list_windows());
91+
},
92+
"Open window with props"
93+
}
94+
button {
95+
onclick: move |_| known_windows.set(refresh_handle.list_windows()),
96+
"Refresh list"
97+
}
3598
}
99+
ul {{ window_rows.into_iter() }}
36100
}
37101
}
38102
}

packages/dioxus-native/src/dioxus_application.rs

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ use anyrender::WindowRenderer;
22
use blitz_shell::{BlitzApplication, View};
33
use dioxus_core::{provide_context, ScopeId};
44
use dioxus_history::{History, MemoryHistory};
5+
use std::cell::RefCell;
56
use std::rc::Rc;
67
use std::sync::Arc;
78
use winit::application::ApplicationHandler;
89
use winit::event::{StartCause, WindowEvent};
910
use winit::event_loop::{ActiveEventLoop, EventLoopProxy};
1011
use winit::window::WindowId;
1112

12-
use crate::windowing::{DioxusWindowHandle, DioxusWindowQueue, DioxusWindowTemplate};
13+
use crate::windowing::{
14+
DioxusWindowHandle, DioxusWindowInfo, DioxusWindowQueue, DioxusWindowTemplate,
15+
};
1316
use crate::DioxusNativeWindowRenderer;
1417
use crate::{contexts::DioxusNativeDocument, BlitzShellEvent, DioxusDocument, WindowConfig};
1518

@@ -22,6 +25,12 @@ pub enum DioxusNativeEvent {
2225
/// Internal signal to create queued windows.
2326
SpawnQueuedWindows,
2427

28+
/// Focus an existing window by id.
29+
FocusWindow { window_id: WindowId },
30+
31+
/// Update a window's title.
32+
SetWindowTitle { window_id: WindowId, title: String },
33+
2534
/// Create a new head element from the Link and Title elements
2635
///
2736
/// todo(jon): these should probabkly be synchronous somehow
@@ -34,26 +43,29 @@ pub enum DioxusNativeEvent {
3443
}
3544

3645
pub struct DioxusNativeApplication {
37-
pending_window: Option<WindowConfig<DioxusNativeWindowRenderer>>,
46+
pending_window: Option<(WindowConfig<DioxusNativeWindowRenderer>, String)>,
3847
inner: BlitzApplication<DioxusNativeWindowRenderer>,
3948
proxy: EventLoopProxy<BlitzShellEvent>,
4049
window_template: Arc<DioxusWindowTemplate>,
4150
window_queue: Rc<DioxusWindowQueue>,
51+
window_registry: Rc<RefCell<Vec<DioxusWindowInfo>>>,
4252
}
4353

4454
impl DioxusNativeApplication {
4555
pub(crate) fn new(
4656
proxy: EventLoopProxy<BlitzShellEvent>,
47-
config: WindowConfig<DioxusNativeWindowRenderer>,
57+
pending_window: (WindowConfig<DioxusNativeWindowRenderer>, String),
4858
window_template: Arc<DioxusWindowTemplate>,
4959
window_queue: Rc<DioxusWindowQueue>,
60+
window_registry: Rc<RefCell<Vec<DioxusWindowInfo>>>,
5061
) -> Self {
5162
Self {
52-
pending_window: Some(config),
63+
pending_window: Some(pending_window),
5364
inner: BlitzApplication::new(proxy.clone()),
5465
proxy,
5566
window_template,
5667
window_queue,
68+
window_registry,
5769
}
5870
}
5971

@@ -64,6 +76,7 @@ impl DioxusNativeApplication {
6476
fn spawn_window(
6577
&mut self,
6678
config: WindowConfig<DioxusNativeWindowRenderer>,
79+
label: String,
6780
event_loop: &ActiveEventLoop,
6881
auto_resume: bool,
6982
) {
@@ -76,6 +89,7 @@ impl DioxusNativeApplication {
7689
}
7790
}
7891
let window_id = window.window_id();
92+
self.register_window(window_id, label);
7993
self.inner.windows.insert(window_id, window);
8094
}
8195

@@ -94,6 +108,7 @@ impl DioxusNativeApplication {
94108
self.proxy.clone(),
95109
Arc::clone(&self.window_template),
96110
Rc::clone(&self.window_queue),
111+
Rc::clone(&self.window_registry),
97112
);
98113
doc.vdom
99114
.in_scope(ScopeId::ROOT, || provide_context(window_handle.clone()));
@@ -114,8 +129,29 @@ impl DioxusNativeApplication {
114129
}
115130

116131
fn drain_window_queue(&mut self, event_loop: &ActiveEventLoop) {
117-
for config in self.window_queue.drain() {
118-
self.spawn_window(config, event_loop, true);
132+
for queued in self.window_queue.drain() {
133+
self.spawn_window(queued.config, queued.title, event_loop, true);
134+
}
135+
}
136+
137+
fn register_window(&self, window_id: WindowId, title: String) {
138+
self.window_registry.borrow_mut().push(DioxusWindowInfo {
139+
id: window_id,
140+
title,
141+
});
142+
}
143+
144+
fn unregister_window(&self, window_id: WindowId) {
145+
let mut registry = self.window_registry.borrow_mut();
146+
registry.retain(|info| info.id != window_id);
147+
}
148+
149+
fn update_window_title(&self, window_id: WindowId, title: String) {
150+
for info in self.window_registry.borrow_mut().iter_mut() {
151+
if info.id == window_id {
152+
info.title = title.clone();
153+
break;
154+
}
119155
}
120156
}
121157

@@ -164,6 +200,19 @@ impl DioxusNativeApplication {
164200
}
165201
}
166202

203+
DioxusNativeEvent::FocusWindow { window_id } => {
204+
if let Some(window) = self.inner.windows.get_mut(window_id) {
205+
window.window.focus_window();
206+
}
207+
}
208+
209+
DioxusNativeEvent::SetWindowTitle { window_id, title } => {
210+
if let Some(window) = self.inner.windows.get_mut(window_id) {
211+
window.window.set_title(title);
212+
self.update_window_title(*window_id, title.to_string());
213+
}
214+
}
215+
167216
DioxusNativeEvent::SpawnQueuedWindows => {
168217
self.drain_window_queue(event_loop);
169218
}
@@ -184,8 +233,8 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
184233
#[cfg(feature = "tracing")]
185234
tracing::debug!("Injecting document provider into all windows");
186235

187-
if let Some(config) = self.pending_window.take() {
188-
self.spawn_window(config, event_loop, false);
236+
if let Some((config, label)) = self.pending_window.take() {
237+
self.spawn_window(config, label, event_loop, false);
189238
}
190239

191240
self.drain_window_queue(event_loop);
@@ -206,6 +255,9 @@ impl ApplicationHandler<BlitzShellEvent> for DioxusNativeApplication {
206255
window_id: WindowId,
207256
event: WindowEvent,
208257
) {
258+
if matches!(event, WindowEvent::CloseRequested) {
259+
self.unregister_window(window_id);
260+
}
209261
self.inner.window_event(event_loop, window_id, event);
210262
}
211263

packages/dioxus-native/src/lib.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use dioxus_native_dom::*;
2727
use assets::DioxusNativeNetProvider;
2828
pub use dioxus_application::{DioxusNativeApplication, DioxusNativeEvent};
2929
pub use dioxus_renderer::DioxusNativeWindowRenderer;
30-
pub use windowing::DioxusWindowHandle;
30+
pub use windowing::{DioxusWindowHandle, DioxusWindowInfo, DioxusWindowOptions};
3131

3232
#[cfg(any(
3333
feature = "vello",
@@ -45,6 +45,7 @@ use blitz_shell::{create_default_event_loop, BlitzShellEvent, Config, WindowConf
4545
use dioxus_core::{ComponentFunction, Element, VirtualDom};
4646
use link_handler::DioxusNativeNavigationProvider;
4747
use std::any::Any;
48+
use std::cell::RefCell;
4849
use std::rc::Rc;
4950
use std::sync::Arc;
5051
use windowing::{DioxusWindowQueue, DioxusWindowTemplate};
@@ -220,6 +221,7 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
220221
renderer.clone(),
221222
window_attributes.clone(),
222223
);
224+
let initial_title = window_attributes.title.clone();
223225
// Create application
224226
let template = Arc::new(DioxusWindowTemplate::new(
225227
contexts,
@@ -230,8 +232,14 @@ pub fn launch_cfg_with_props<P: Clone + 'static, M: 'static>(
230232
navigation_provider.clone(),
231233
));
232234
let window_queue = Rc::new(DioxusWindowQueue::new());
233-
let mut application =
234-
DioxusNativeApplication::new(event_loop.create_proxy(), config, template, window_queue);
235+
let window_registry = Rc::new(RefCell::new(Vec::new()));
236+
let mut application = DioxusNativeApplication::new(
237+
event_loop.create_proxy(),
238+
(config, initial_title),
239+
template,
240+
window_queue,
241+
window_registry,
242+
);
235243

236244
// Run event loop
237245
event_loop.run_app(&mut application).unwrap();

0 commit comments

Comments
 (0)