Added inner_size syncing

This adds upon the work in #94 by allowing the dynamic's value to be set
and it cause the window size to change.

I've ordered the resizing operations so that changes to the property are
prioritized over automatic adjustments. This doesn't change the behavior
for automatic adjustments in any way.
This commit is contained in:
Jonathan Johnson 2023-12-19 13:33:45 -08:00
parent f9d0203ff5
commit 873e6d6f16
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
2 changed files with 136 additions and 65 deletions

View file

@ -369,4 +369,17 @@ impl FontState {
}) })
.cloned() .cloned()
} }
pub fn apply_font_family_list(
&self,
family: &FontFamilyList,
fallback: impl FnOnce() -> Option<FamilyOwned>,
apply: impl FnOnce(String),
) {
if let Some(FamilyOwned::Name(name)) =
self.find_available_font_family(family).or_else(fallback)
{
apply(name);
}
}
} }

View file

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