diff --git a/src/graphics.rs b/src/graphics.rs index f3cd6d3..2628dd5 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -369,4 +369,17 @@ impl FontState { }) .cloned() } + + pub fn apply_font_family_list( + &self, + family: &FontFamilyList, + fallback: impl FnOnce() -> Option, + apply: impl FnOnce(String), + ) { + if let Some(FamilyOwned::Name(name)) = + self.find_available_font_family(family).or_else(fallback) + { + apply(name); + } + } } diff --git a/src/window.rs b/src/window.rs index ad5e06b..76b539c 100644 --- a/src/window.rs +++ b/src/window.rs @@ -36,7 +36,7 @@ use crate::graphics::{FontState, Graphics}; use crate::styles::{Edges, FontFamilyList, ThemePair}; use crate::tree::Tree; use crate::utils::ModifiersExt; -use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value}; +use crate::value::{Dynamic, DynamicReader, Generation, IntoDynamic, IntoValue, Value}; use crate::widget::{ EventHandling, MountedWidget, RootBehavior, Widget, WidgetId, WidgetInstance, HANDLED, IGNORED, }; @@ -79,12 +79,16 @@ impl<'window> RunningWindow<'window> { /// Returns a dynamic that is updated whenever this window's occlusion /// status changes. #[must_use] - pub fn occluded(&self) -> &Dynamic { + pub const fn occluded(&self) -> &Dynamic { &self.occluded } - /// Returns a dynamic that is updated whenever this window's inner size - /// changes. + /// Returns a dynamic that is synchronized with this window's inner size. + /// + /// Whenever the window is resized, this dynamic will be updated with the + /// new inner size. Setting a new value will request the new size from the + /// operating system, but resize requests may be altered or ignored by the + /// operating system. #[must_use] pub const fn inner_size(&self) -> &Dynamic> { &self.inner_size @@ -205,11 +209,12 @@ impl Window { self } - /// Sets `inner_size` to be the dynamic updated when this window's inner size - /// is changed. + /// Sets `inner_size` to be the dynamic syncrhonized with this window's + /// inner size. /// - /// When the window is resized, the dynamic will contain its new size. - /// `true`. + /// When the window is resized, the dynamic will contain its new size. When + /// the dynamic is updated with a new value, a resize request will be made + /// with the new inner size. pub fn inner_size(mut self, inner_size: impl IntoDynamic>) -> Self { let inner_size = inner_size.into_dynamic(); self.inner_size = Some(inner_size); @@ -371,6 +376,7 @@ struct GooeyWindow { occluded: Dynamic, focused: Dynamic, inner_size: Dynamic>, + inner_size_generation: Generation, keyboard_activated: Option, min_inner_size: Option>, max_inner_size: Option>, @@ -573,8 +579,10 @@ where let gooey = settings.gooey.clone(); let occluded = settings.occluded.take().unwrap_or_default(); let focused = settings.focused.take().unwrap_or_default(); - let inner_size = settings.inner_size.take().unwrap_or_default(); let theme = settings.theme.take().expect("theme always present"); + let inner_size = settings.inner_size.take().unwrap_or_default(); + + inner_size.set(window.inner_size()); let fontdb = graphics.font_system().db_mut(); for font_to_load in settings.font_data_to_load.drain(..) { @@ -582,55 +590,47 @@ where } let fonts = FontState::new(fontdb); + fonts.apply_font_family_list( + &settings.serif_font_family, + || default_family(Family::Serif), + |name| fontdb.set_serif_family(name), + ); - if let Some(FamilyOwned::Name(name)) = fonts - .find_available_font_family(&settings.serif_font_family) - .or_else(|| default_family(Family::Serif)) - { - fontdb.set_serif_family(name); - } - - let bundled_font_name; - #[cfg(feature = "roboto-flex")] - { - bundled_font_name = Some(String::from("Roboto Flex")); - } - #[cfg(not(feature = "roboto-flex"))] - { - bundled_font_name = None; - } - - if let Some(FamilyOwned::Name(name)) = fonts - .find_available_font_family(&settings.sans_serif_font_family) - .or_else(|| { - if let Some(name) = bundled_font_name { - Some(FamilyOwned::Name(name)) - } else { - default_family(Family::SansSerif) + fonts.apply_font_family_list( + &settings.sans_serif_font_family, + || { + let bundled_font_name; + #[cfg(feature = "roboto-flex")] + { + bundled_font_name = Some(String::from("Roboto Flex")); + } + #[cfg(not(feature = "roboto-flex"))] + { + bundled_font_name = None; } - }) - { - fontdb.set_sans_serif_family(name); - } - if let Some(FamilyOwned::Name(name)) = fonts - .find_available_font_family(&settings.fantasy_font_family) - .or_else(|| default_family(Family::Fantasy)) - { - fontdb.set_fantasy_family(name); - } - if let Some(FamilyOwned::Name(name)) = fonts - .find_available_font_family(&settings.monospace_font_family) - .or_else(|| default_family(Family::Monospace)) - { - fontdb.set_monospace_family(name); - } - if let Some(FamilyOwned::Name(name)) = fonts - .find_available_font_family(&settings.cursive_font_family) - .or_else(|| default_family(Family::Cursive)) - { - fontdb.set_cursive_family(name); - } + bundled_font_name.map_or_else( + || default_family(Family::SansSerif), + |name| Some(FamilyOwned::Name(name)), + ) + }, + |name| fontdb.set_sans_serif_family(name), + ); + fonts.apply_font_family_list( + &settings.fantasy_font_family, + || default_family(Family::Fantasy), + |name| fontdb.set_fantasy_family(name), + ); + fonts.apply_font_family_list( + &settings.monospace_font_family, + || default_family(Family::Monospace), + |name| fontdb.set_monospace_family(name), + ); + fonts.apply_font_family_list( + &settings.cursive_font_family, + || default_family(Family::Cursive), + |name| fontdb.set_cursive_family(name), + ); let theme_mode = match settings.theme_mode.take() { Some(Value::Dynamic(dynamic)) => { @@ -666,6 +666,7 @@ where initial_frame: true, occluded, focused, + inner_size_generation: inner_size.generation(), inner_size, keyboard_activated: None, min_inner_size: None, @@ -698,7 +699,13 @@ where .new_frame(self.redraw_status.invalidations().drain()); let resizable = window.winit().is_resizable(); - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics); self.fonts.next_frame(); @@ -741,7 +748,14 @@ where layout_size }; let render_size = actual_size.min(window_size); - if actual_size != window_size && !resizable { + layout_context.redraw_when_changed(&self.inner_size); + let inner_size_generation = self.inner_size.generation(); + if self.inner_size_generation != inner_size_generation { + let _ = layout_context + .winit() + .request_inner_size(PhysicalSize::from(self.inner_size.get())); + self.inner_size_generation = inner_size_generation; + } else if actual_size != window_size && !resizable { let mut new_size = actual_size; if let Some(min_size) = self.min_inner_size { new_size = new_size.max(min_size); @@ -825,7 +839,13 @@ where Self::request_close( &mut self.should_close, &mut self.behavior, - &mut RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size), + &mut RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ), ) } @@ -865,6 +885,8 @@ where _kludgine: &mut Kludgine, ) { self.inner_size.set(window.inner_size()); + // We want to prevent a resize request for this resized event. + self.inner_size_generation = self.inner_size.generation(); self.root.invalidate(); } @@ -890,7 +912,13 @@ where let Some(target) = self.root.tree.widget_from_node(target) else { return; }; - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); let mut target = EventContext::new( WidgetContext::new( target, @@ -1000,7 +1028,13 @@ where .expect("missing widget") }); - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); let mut widget = EventContext::new( WidgetContext::new( widget, @@ -1036,7 +1070,13 @@ where .widget(self.root.id()) .expect("missing widget") }); - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); let mut target = EventContext::new( WidgetContext::new( widget, @@ -1063,7 +1103,13 @@ where let location = Point::::from(position); self.cursor.location = Some(location); - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); EventContext::new( WidgetContext::new( @@ -1110,7 +1156,13 @@ where _device_id: DeviceId, ) { if self.cursor.widget.take().is_some() { - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); let mut context = EventContext::new( WidgetContext::new( self.root.clone(), @@ -1134,7 +1186,13 @@ where state: ElementState, button: MouseButton, ) { - let mut window = RunningWindow::new(window, &self.gooey, &self.focused, &self.occluded, &self.inner_size); + let mut window = RunningWindow::new( + window, + &self.gooey, + &self.focused, + &self.occluded, + &self.inner_size, + ); match state { ElementState::Pressed => { EventContext::new( @@ -1266,8 +1324,8 @@ pub(crate) struct CursorState { pub(crate) mod sealed { use std::cell::RefCell; - use kludgine::figures::Size; use kludgine::figures::units::UPx; + use kludgine::figures::Size; use crate::styles::{FontFamilyList, ThemePair}; use crate::value::{Dynamic, Value};