mirror of
https://github.com/danbulant/cushy
synced 2026-07-06 19:50:40 +00:00
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:
parent
8f99ae19fd
commit
a3e45d1d86
7 changed files with 98 additions and 23 deletions
|
|
@ -17,7 +17,7 @@ fn main() -> gooey::Result {
|
||||||
AppState::Winner(winner) => game_end(*winner, &app).make_widget(),
|
AppState::Winner(winner) => game_end(*winner, &app).make_widget(),
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.switcher()
|
.into_switcher()
|
||||||
.contain()
|
.contain()
|
||||||
.width(Lp::inches(2)..Lp::inches(6))
|
.width(Lp::inches(2)..Lp::inches(6))
|
||||||
.height(Lp::inches(2)..Lp::inches(6))
|
.height(Lp::inches(2)..Lp::inches(6))
|
||||||
|
|
|
||||||
|
|
@ -623,15 +623,6 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
|
||||||
self.gfx.set_font_weight(self.widget.get(&FontWeight));
|
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
|
/// Invokes [`Widget::redraw()`](crate::widget::Widget::redraw) on this
|
||||||
/// context's widget.
|
/// context's widget.
|
||||||
///
|
///
|
||||||
|
|
@ -1270,6 +1261,14 @@ pub trait ManageWidget {
|
||||||
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed;
|
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 {
|
impl ManageWidget for WidgetInstance {
|
||||||
type Managed = Option<ManagedWidget>;
|
type Managed = Option<ManagedWidget>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use crate::styles::FontFamilyList;
|
||||||
pub struct Graphics<'clip, 'gfx, 'pass> {
|
pub struct Graphics<'clip, 'gfx, 'pass> {
|
||||||
renderer: RenderContext<'clip, 'gfx, 'pass>,
|
renderer: RenderContext<'clip, 'gfx, 'pass>,
|
||||||
region: Rect<Px>,
|
region: Rect<Px>,
|
||||||
|
current_font_family: &'clip mut Option<FontFamilyList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RenderContext<'clip, 'gfx, 'pass> {
|
enum RenderContext<'clip, 'gfx, 'pass> {
|
||||||
|
|
@ -29,10 +30,14 @@ enum RenderContext<'clip, 'gfx, 'pass> {
|
||||||
impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> {
|
||||||
/// Returns a new graphics context for the given [`Renderer`].
|
/// Returns a new graphics context for the given [`Renderer`].
|
||||||
#[must_use]
|
#[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 {
|
Self {
|
||||||
region: renderer.clip_rect().into_signed(),
|
region: renderer.clip_rect().into_signed(),
|
||||||
renderer: RenderContext::Renderer(renderer),
|
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`.
|
/// Sets the font family to the first family in `list`.
|
||||||
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
|
pub fn set_available_font_family(&mut self, list: &FontFamilyList) {
|
||||||
if let Some(family) = self.find_available_font_family(list) {
|
if self.current_font_family.as_ref() != Some(list) {
|
||||||
self.set_font_family(family);
|
*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 {
|
Graphics {
|
||||||
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
|
renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)),
|
||||||
region,
|
region,
|
||||||
|
current_font_family: &mut *self.current_font_family,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
61
src/value.rs
61
src/value.rs
|
|
@ -1,7 +1,7 @@
|
||||||
//! Types for storing and interacting with values in Widgets.
|
//! Types for storing and interacting with values in Widgets.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{self, Debug, Display};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::hash::{BuildHasher, Hash};
|
use std::hash::{BuildHasher, Hash};
|
||||||
use std::ops::{Deref, DerefMut, Not};
|
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
|
/// An instance of a value that provides APIs to observe and react to its
|
||||||
/// contents.
|
/// contents.
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Dynamic<T>(Arc<DynamicData<T>>);
|
pub struct Dynamic<T>(Arc<DynamicData<T>>);
|
||||||
|
|
||||||
impl<T> Dynamic<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> {
|
impl Dynamic<WidgetInstance> {
|
||||||
/// Returns a new [`Switcher`] widget whose contents is the value of this
|
/// Returns a new [`Switcher`] widget whose contents is the value of this
|
||||||
/// dynamic.
|
/// dynamic.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn switcher(self) -> Switcher {
|
pub fn into_switcher(self) -> Switcher {
|
||||||
Switcher::new(self)
|
Switcher::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MakeWidgetWithId for Dynamic<WidgetInstance> {
|
impl MakeWidgetWithId for Dynamic<WidgetInstance> {
|
||||||
fn make_with_id(self, id: crate::widget::WidgetTag) -> 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> {
|
impl<T> PartialEq for Dynamic<T> {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
Arc::ptr_eq(&self.0, &other.0)
|
Arc::ptr_eq(&self.0, &other.0)
|
||||||
|
|
@ -833,7 +850,7 @@ struct DeadlockError;
|
||||||
impl std::error::Error for DeadlockError {}
|
impl std::error::Error for DeadlockError {}
|
||||||
|
|
||||||
impl Display 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")
|
f.write_str("a deadlock was detected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -869,7 +886,7 @@ impl<T> Debug for State<T>
|
||||||
where
|
where
|
||||||
T: Debug,
|
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")
|
f.debug_struct("State")
|
||||||
.field("wrapped", &self.wrapped)
|
.field("wrapped", &self.wrapped)
|
||||||
.field("readers", &self.readers)
|
.field("readers", &self.readers)
|
||||||
|
|
@ -988,6 +1005,17 @@ pub struct DynamicGuard<'a, T> {
|
||||||
accessed_mut: bool,
|
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> {
|
impl<'a, T> Deref for DynamicGuard<'a, T> {
|
||||||
type Target = 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.
|
/// A grouping of validations that can be checked simultaneously.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Validations {
|
pub struct Validations {
|
||||||
|
|
@ -2135,13 +2176,17 @@ impl WhenValidation<'_> {
|
||||||
/// The validation is linked with `self` such that checking `self`'s
|
/// The validation is linked with `self` such that checking `self`'s
|
||||||
/// validation status will include this validation.
|
/// validation status will include this validation.
|
||||||
#[must_use]
|
#[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
|
where
|
||||||
|
T: Send + 'static,
|
||||||
E: Display + Send + 'static,
|
E: Display + Send + 'static,
|
||||||
{
|
{
|
||||||
let result = result.into_dynamic();
|
let result = result.into_dynamic();
|
||||||
let error_message = result.map_each(move |value| match value {
|
let error_message = result.map_each(move |value| match value {
|
||||||
Ok(()) => None,
|
Ok(_) => None,
|
||||||
Err(err) => Some(err.to_string()),
|
Err(err) => Some(err.to_string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
impl<T, const N: usize> From<T> for GridWidgets<N>
|
||||||
where
|
where
|
||||||
T: Into<GridSection<N>>,
|
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> {
|
impl<const N: usize> Deref for GridSection<N> {
|
||||||
type Target = [WidgetInstance; N];
|
type Target = [WidgetInstance; N];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1099,7 +1099,7 @@ where
|
||||||
.translate_by(padding),
|
.translate_by(padding),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if window_focused {
|
} else if window_focused && context.enabled() {
|
||||||
let (location, _) =
|
let (location, _) =
|
||||||
Self::point_from_cursor(cache.measured, cache.cursor, cache.bytes);
|
Self::point_from_cursor(cache.measured, cache.cursor, cache.bytes);
|
||||||
if cursor_state.visible {
|
if cursor_state.visible {
|
||||||
|
|
|
||||||
|
|
@ -637,6 +637,7 @@ where
|
||||||
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
|
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
|
||||||
|
|
||||||
let graphics = self.contents.new_frame(graphics);
|
let graphics = self.contents.new_frame(graphics);
|
||||||
|
let mut current_font_family = None;
|
||||||
let mut context = GraphicsContext {
|
let mut context = GraphicsContext {
|
||||||
widget: WidgetContext::new(
|
widget: WidgetContext::new(
|
||||||
self.root.clone(),
|
self.root.clone(),
|
||||||
|
|
@ -646,7 +647,7 @@ where
|
||||||
self.theme_mode.get(),
|
self.theme_mode.get(),
|
||||||
&mut self.cursor,
|
&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);
|
self.theme_mode.redraw_when_changed(&context);
|
||||||
let mut layout_context = LayoutContext::new(&mut context);
|
let mut layout_context = LayoutContext::new(&mut context);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue