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()
}
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::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<bool> {
pub const fn occluded(&self) -> &Dynamic<bool> {
&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<Size<UPx>> {
&self.inner_size
@ -205,11 +209,12 @@ impl Window<WidgetInstance> {
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<Size<UPx>>) -> Self {
let inner_size = inner_size.into_dynamic();
self.inner_size = Some(inner_size);
@ -371,6 +376,7 @@ struct GooeyWindow<T> {
occluded: Dynamic<bool>,
focused: Dynamic<bool>,
inner_size: Dynamic<Size<UPx>>,
inner_size_generation: Generation,
keyboard_activated: Option<WidgetId>,
min_inner_size: Option<Size<UPx>>,
max_inner_size: Option<Size<UPx>>,
@ -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::<Px>::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};