Small improvements

- Caching font family resolution to avoid scanning the database over and
  over. The db should still be cached, but this makes repeated setting
  free.
- into_switcher rename for Dynamic<WidgetInstance> to avoid conflicting
  with Switchable::switcher()
- Dynamic debugging is less verbose
- IntoDynamic<Validation> for Result<T,E>
- Input no longer blinks cursor when disabled.
This commit is contained in:
Jonathan Johnson 2023-12-01 12:52:46 -08:00
parent 8f99ae19fd
commit a3e45d1d86
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
7 changed files with 98 additions and 23 deletions

View file

@ -17,7 +17,7 @@ fn main() -> gooey::Result {
AppState::Winner(winner) => game_end(*winner, &app).make_widget(),
}
}))
.switcher()
.into_switcher()
.contain()
.width(Lp::inches(2)..Lp::inches(6))
.height(Lp::inches(2)..Lp::inches(6))

View file

@ -623,15 +623,6 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
self.gfx.set_font_weight(self.widget.get(&FontWeight));
}
// /// Applies the current style settings for font family, text size, font
// /// style, and font weight.
// pub fn apply_current_font_settings_to<'a>(&self, attrs: Attrs<'a>) -> Attrs<'a> {
// attrs.set_available_font_family(&self.widget.get(&FontFamily));
// attrs.set_font_size(self.widget.get(&TextSize));
// attrs.set_font_style(self.widget.get(&FontStyle));
// attrs.set_font_weight(self.widget.get(&FontWeight));
// }
/// Invokes [`Widget::redraw()`](crate::widget::Widget::redraw) on this
/// context's widget.
///
@ -1270,6 +1261,14 @@ pub trait ManageWidget {
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed;
}
impl ManageWidget for WidgetId {
type Managed = Option<ManagedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
context.current_node.tree.widget(*self)
}
}
impl ManageWidget for WidgetInstance {
type Managed = Option<ManagedWidget>;

View file

@ -19,6 +19,7 @@ use crate::styles::FontFamilyList;
pub struct Graphics<'clip, 'gfx, 'pass> {
renderer: RenderContext<'clip, 'gfx, 'pass>,
region: Rect<Px>,
current_font_family: &'clip mut Option<FontFamilyList>,
}
enum RenderContext<'clip, 'gfx, 'pass> {
@ -29,10 +30,14 @@ enum RenderContext<'clip, 'gfx, 'pass> {
impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
/// Returns a new graphics context for the given [`Renderer`].
#[must_use]
pub fn new(renderer: Renderer<'gfx, 'pass>) -> Self {
pub fn new(
renderer: Renderer<'gfx, 'pass>,
current_font_family: &'clip mut Option<FontFamilyList>,
) -> Self {
Self {
region: renderer.clip_rect().into_signed(),
renderer: RenderContext::Renderer(renderer),
current_font_family,
}
}
@ -86,8 +91,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
/// Sets the font family to the first family in `list`.
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
if let Some(family) = self.find_available_font_family(list) {
self.set_font_family(family);
if self.current_font_family.as_ref() != Some(list) {
*self.current_font_family = Some(list.clone());
if let Some(family) = self.find_available_font_family(list) {
self.set_font_family(family);
}
}
}
@ -123,6 +131,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
Graphics {
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
region,
current_font_family: &mut *self.current_font_family,
}
}

View file

@ -1,7 +1,7 @@
//! Types for storing and interacting with values in Widgets.
use std::collections::HashMap;
use std::fmt::{Debug, Display};
use std::fmt::{self, Debug, Display};
use std::future::Future;
use std::hash::{BuildHasher, Hash};
use std::ops::{Deref, DerefMut, Not};
@ -25,7 +25,6 @@ use crate::widgets::{Radio, Select, Space, Switcher};
/// An instance of a value that provides APIs to observe and react to its
/// contents.
#[derive(Debug)]
pub struct Dynamic<T>(Arc<DynamicData<T>>);
impl<T> Dynamic<T> {
@ -598,18 +597,34 @@ impl<T> Dynamic<T> {
}
}
impl<T> Debug for Dynamic<T>
where
T: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0.state() {
Ok(state) => f
.debug_struct("Dynamic")
.field("value", &state.wrapped.value)
.field("generation", &state.wrapped.generation)
.finish(),
Err(_) => f.debug_tuple("Dynamic").field(&"<unable to lock>").finish(),
}
}
}
impl Dynamic<WidgetInstance> {
/// Returns a new [`Switcher`] widget whose contents is the value of this
/// dynamic.
#[must_use]
pub fn switcher(self) -> Switcher {
pub fn into_switcher(self) -> Switcher {
Switcher::new(self)
}
}
impl MakeWidgetWithId for Dynamic<WidgetInstance> {
fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance {
self.switcher().make_with_id(id)
self.into_switcher().make_with_id(id)
}
}
@ -634,6 +649,8 @@ impl<T> context::sealed::Trackable for Dynamic<T> {
}
}
impl<T> Eq for Dynamic<T> {}
impl<T> PartialEq for Dynamic<T> {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
@ -833,7 +850,7 @@ struct DeadlockError;
impl std::error::Error for DeadlockError {}
impl Display for DeadlockError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a deadlock was detected")
}
}
@ -869,7 +886,7 @@ impl<T> Debug for State<T>
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("State")
.field("wrapped", &self.wrapped)
.field("readers", &self.readers)
@ -988,6 +1005,17 @@ pub struct DynamicGuard<'a, T> {
accessed_mut: bool,
}
impl<T> DynamicGuard<'_, T> {
/// Returns the generation of the value at the time of locking the dynamic.
///
/// Even if this guard accesses the data through [`DerefMut`], this value
/// will remain unchanged while the guard is held.
#[must_use]
pub fn generation(&self) -> Generation {
self.guard.wrapped.generation
}
}
impl<'a, T> Deref for DynamicGuard<'a, T> {
type Target = T;
@ -1896,6 +1924,19 @@ impl Validation {
}
}
impl<T, E> IntoDynamic<Validation> for Dynamic<Result<T, E>>
where
T: Send + 'static,
E: Display + Send + 'static,
{
fn into_dynamic(self) -> Dynamic<Validation> {
self.map_each(|result| match result {
Ok(_) => Validation::Valid,
Err(err) => Validation::Invalid(err.to_string()),
})
}
}
/// A grouping of validations that can be checked simultaneously.
#[derive(Debug, Default, Clone)]
pub struct Validations {
@ -2135,13 +2176,17 @@ impl WhenValidation<'_> {
/// The validation is linked with `self` such that checking `self`'s
/// validation status will include this validation.
#[must_use]
pub fn validate_result<E>(&self, result: impl IntoDynamic<Result<(), E>>) -> Dynamic<Validation>
pub fn validate_result<T, E>(
&self,
result: impl IntoDynamic<Result<T, E>>,
) -> Dynamic<Validation>
where
T: Send + 'static,
E: Display + Send + 'static,
{
let result = result.into_dynamic();
let error_message = result.map_each(move |value| match value {
Ok(()) => None,
Ok(_) => None,
Err(err) => Some(err.to_string()),
});

View file

@ -800,6 +800,15 @@ impl<const N: usize> GridWidgets<N> {
}
}
impl<T, const N: usize> From<Vec<T>> for GridWidgets<N>
where
T: Into<GridSection<N>>,
{
fn from(value: Vec<T>) -> Self {
Self(value.into_iter().map(T::into).collect())
}
}
impl<T, const N: usize> From<T> for GridWidgets<N>
where
T: Into<GridSection<N>>,
@ -851,6 +860,18 @@ where
}
}
impl<const N: usize, T> From<[T; N]> for GridSection<N>
where
T: MakeWidget,
{
fn from(values: [T; N]) -> Self {
let mut widgets = values.into_iter();
Self(array::from_fn(|_| {
widgets.next().assert("length checked").make_widget()
}))
}
}
impl<const N: usize> Deref for GridSection<N> {
type Target = [WidgetInstance; N];

View file

@ -1099,7 +1099,7 @@ where
.translate_by(padding),
);
}
} else if window_focused {
} else if window_focused && context.enabled() {
let (location, _) =
Self::point_from_cursor(cache.measured, cache.cursor, cache.bytes);
if cursor_state.visible {

View file

@ -637,6 +637,7 @@ where
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
let graphics = self.contents.new_frame(graphics);
let mut current_font_family = None;
let mut context = GraphicsContext {
widget: WidgetContext::new(
self.root.clone(),
@ -646,7 +647,7 @@ where
self.theme_mode.get(),
&mut self.cursor,
),
gfx: Exclusive::Owned(Graphics::new(graphics)),
gfx: Exclusive::Owned(Graphics::new(graphics, &mut current_font_family)),
};
self.theme_mode.redraw_when_changed(&context);
let mut layout_context = LayoutContext::new(&mut context);