From fc165628e51b5c7ff167320c9a5f94855a8a6b47 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 8 Nov 2023 08:44:16 -0800 Subject: [PATCH] Input no longer blinks in the background Also, Expand now expands properly. --- examples/input.rs | 4 +- src/context.rs | 13 +++++ src/lib.rs | 18 ++++++ src/widgets/expand.rs | 13 ++++- src/widgets/input.rs | 9 ++- src/window.rs | 128 ++++++++++++++++++++++++++++++++++-------- 6 files changed, 155 insertions(+), 30 deletions(-) diff --git a/examples/input.rs b/examples/input.rs index 8b042f4..94ae2a1 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -1,6 +1,6 @@ -use gooey::widgets::Input; +use gooey::widgets::{Expand, Input}; use gooey::Run; fn main() -> gooey::Result { - Input::new("Hello").run() + Expand::new(Input::new("Hello")).run() } diff --git a/src/context.rs b/src/context.rs index 1b8e2c0..820ae1f 100644 --- a/src/context.rs +++ b/src/context.rs @@ -858,6 +858,18 @@ impl<'context, 'window> WidgetContext<'context, 'window> { redraw_status: self.redraw_status.clone(), } } + + /// Returns the window containing this widget. + #[must_use] + pub fn window(&self) -> &RunningWindow<'window> { + self.window + } + + /// Returns an exclusive reference to the window containing this widget. + #[must_use] + pub fn window_mut(&mut self) -> &mut RunningWindow<'window> { + self.window + } } pub(crate) struct WindowHandle { @@ -890,6 +902,7 @@ impl<'window> Deref for WidgetContext<'_, 'window> { self.window } } + impl<'window> DerefMut for WidgetContext<'_, 'window> { fn deref_mut(&mut self) -> &mut Self::Target { self.window diff --git a/src/lib.rs b/src/lib.rs index 389dac8..62b3509 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ use std::ops::Sub; pub use kludgine; use kludgine::app::winit::error::EventLoopError; use kludgine::figures::units::UPx; +use kludgine::figures::{Fraction, IntoUnsigned, ScreenUnit}; pub use names::Name; pub use utils::WithClone; @@ -44,6 +45,23 @@ impl ConstraintLimit { ConstraintLimit::Known(v) | ConstraintLimit::ClippedAfter(v) => v, } } + + /// Converts `measured` to unsigned pixels, and adjusts it according to the + /// contraint's intentions. + /// + /// If this constraint is of a known size, it will return the maximum of the + /// measured size and the contraint. If it is of an unknown size, it will + /// return the measured size. + pub fn fit_measured(self, measured: Unit, scale: Fraction) -> UPx + where + Unit: ScreenUnit, + { + let measured = measured.into_px(scale).into_unsigned(); + match self { + ConstraintLimit::Known(size) => size.max(measured), + ConstraintLimit::ClippedAfter(_) => measured, + } + } } impl Sub for ConstraintLimit { diff --git a/src/widgets/expand.rs b/src/widgets/expand.rs index 6eef246..6d265ff 100644 --- a/src/widgets/expand.rs +++ b/src/widgets/expand.rs @@ -63,7 +63,16 @@ impl Widget for Expand { ); let child = self.child.mounted(&mut context.as_event_context()); let size = context.for_other(&child).layout(available_space); - context.set_child_layout(&child, Rect::from(size.into_signed())); - size + + let expanded_size = Size::new( + available_space + .width + .fit_measured(size.width, context.graphics.scale()), + available_space + .height + .fit_measured(size.height, context.graphics.scale()), + ); + context.set_child_layout(&child, Rect::from(expanded_size.into_signed())); + expanded_size } } diff --git a/src/widgets/input.rs b/src/widgets/input.rs index 3d5f9fc..4a95878 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -280,7 +280,8 @@ impl Widget for Input { (Err(_), Err(_)) => {} } } else if let Ok((location, _)) = cursor_glyph(buffer, &cursor) { - if cursor_state.visible { + let window_focused = context.window().focused().get(); + if window_focused && cursor_state.visible { context.graphics.draw_shape( &Shape::filled_rect( Rect::new( @@ -294,7 +295,11 @@ impl Widget for Input { None, ); } - context.redraw_in(cursor_state.remaining_until_blink); + if window_focused { + context.redraw_in(cursor_state.remaining_until_blink); + } else { + context.redraw_when_changed(context.window().focused()); + } } } diff --git a/src/window.rs b/src/window.rs index 63884a3..e7b22fe 100644 --- a/src/window.rs +++ b/src/window.rs @@ -4,6 +4,7 @@ use std::cell::RefCell; use std::collections::HashMap; use std::ffi::OsStr; +use std::ops::{Deref, DerefMut}; use std::panic::{AssertUnwindSafe, UnwindSafe}; use std::path::Path; use std::string::ToString; @@ -34,7 +35,53 @@ use crate::window::sealed::WindowCommand; use crate::{ConstraintLimit, Run}; /// A currently running Gooey window. -pub type RunningWindow<'window> = kludgine::app::Window<'window, WindowCommand>; +pub struct RunningWindow<'window> { + window: kludgine::app::Window<'window, WindowCommand>, + focused: Dynamic, + occluded: Dynamic, +} + +impl<'window> RunningWindow<'window> { + pub(crate) fn new( + window: kludgine::app::Window<'window, WindowCommand>, + focused: &Dynamic, + occluded: &Dynamic, + ) -> Self { + Self { + window, + focused: focused.clone(), + occluded: occluded.clone(), + } + } + + /// Returns a dynamic that is updated whenever this window's focus status + /// changes. + #[must_use] + pub const fn focused(&self) -> &Dynamic { + &self.focused + } + + /// Returns a dynamic that is updated whenever this window's occlusion + /// status changes. + #[must_use] + pub fn occluded(&self) -> &Dynamic { + &self.occluded + } +} + +impl<'window> Deref for RunningWindow<'window> { + type Target = kludgine::app::Window<'window, WindowCommand>; + + fn deref(&self) -> &Self::Target { + &self.window + } +} + +impl<'window> DerefMut for RunningWindow<'window> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.window + } +} /// The attributes of a Gooey window. pub type WindowAttributes = kludgine::app::WindowAttributes; @@ -194,8 +241,8 @@ struct GooeyWindow { mouse_state: MouseState, redraw_status: RedrawStatus, initial_frame: bool, - occluded: Option>, - focused: Option>, + occluded: Dynamic, + focused: Dynamic, } impl GooeyWindow @@ -216,14 +263,27 @@ where type Context = AssertUnwindSafe>; fn initialize( - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, _graphics: &mut kludgine::Graphics<'_>, AssertUnwindSafe(context): Self::Context, ) -> Self { - let mut behavior = T::initialize(&mut window, context.user); + let occluded = context + .settings + .borrow_mut() + .occluded + .take() + .unwrap_or_default(); + let focused = context + .settings + .borrow_mut() + .focused + .take() + .unwrap_or_default(); + let mut behavior = T::initialize( + &mut RunningWindow::new(window, &focused, &occluded), + context.user, + ); let root = Tree::default().push_boxed(behavior.make_root(), None); - let occluded = context.settings.borrow_mut().occluded.take(); - let focused = context.settings.borrow_mut().focused.take(); Self { behavior, @@ -242,11 +302,16 @@ where } } - fn prepare(&mut self, mut window: RunningWindow<'_>, graphics: &mut kludgine::Graphics<'_>) { + fn prepare( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + graphics: &mut kludgine::Graphics<'_>, + ) { self.redraw_status.refresh_received(); graphics.reset_text_attributes(); self.root.tree.reset_render_order(); let graphics = self.contents.new_frame(graphics); + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut context = GraphicsContext { widget: WidgetContext::new(self.root.clone(), &self.redraw_status, &mut window), graphics: Exclusive::Owned(Graphics::new(graphics)), @@ -280,9 +345,7 @@ where window: kludgine::app::Window<'_, WindowCommand>, _kludgine: &mut Kludgine, ) { - if let Some(focused) = &self.focused { - focused.update(window.focused()); - } + self.focused.update(window.focused()); } fn occlusion_changed( @@ -290,14 +353,12 @@ where window: kludgine::app::Window<'_, WindowCommand>, _kludgine: &mut Kludgine, ) { - if let Some(occluded) = &self.occluded { - occluded.update(window.ocluded()); - } + self.occluded.update(window.ocluded()); } fn render<'pass>( &'pass mut self, - _window: RunningWindow<'_>, + _window: kludgine::app::Window<'_, WindowCommand>, graphics: &mut kludgine::RenderingGraphics<'_, 'pass>, ) -> bool { self.contents.render(graphics); @@ -316,8 +377,16 @@ where .expect("called more than once") } - fn close_requested(&mut self, mut window: RunningWindow<'_>, _kludgine: &mut Kludgine) -> bool { - self.request_close(&mut window) + fn close_requested( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + _kludgine: &mut Kludgine, + ) -> bool { + self.request_close(&mut RunningWindow::new( + window, + &self.focused, + &self.occluded, + )) } // fn power_preference() -> wgpu::PowerPreference { @@ -352,7 +421,7 @@ where fn keyboard_input( &mut self, - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, kludgine: &mut Kludgine, device_id: DeviceId, input: KeyEvent, @@ -360,6 +429,7 @@ where ) { let target = self.root.tree.focused_widget().unwrap_or(self.root.id()); let target = self.root.tree.widget(target).expect("missing widget"); + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut target = EventContext::new( WidgetContext::new(target, &self.redraw_status, &mut window), kludgine, @@ -404,7 +474,7 @@ where fn mouse_wheel( &mut self, - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, kludgine: &mut Kludgine, device_id: DeviceId, delta: MouseScrollDelta, @@ -422,6 +492,7 @@ where .expect("missing widget") }); + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut widget = EventContext::new( WidgetContext::new(widget, &self.redraw_status, &mut window), kludgine, @@ -433,7 +504,12 @@ where // fn modifiers_changed(&mut self, window: kludgine::app::Window<'_, ()>) {} - fn ime(&mut self, mut window: RunningWindow<'_>, kludgine: &mut Kludgine, ime: Ime) { + fn ime( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + kludgine: &mut Kludgine, + ime: Ime, + ) { let widget = self .root .tree @@ -445,6 +521,7 @@ where .widget(self.root.id()) .expect("missing widget") }); + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut target = EventContext::new( WidgetContext::new(widget, &self.redraw_status, &mut window), kludgine, @@ -456,7 +533,7 @@ where fn cursor_moved( &mut self, - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, kludgine: &mut Kludgine, device_id: DeviceId, position: PhysicalPosition, @@ -464,6 +541,7 @@ where let location = Point::::from(position); self.mouse_state.location = Some(location); + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); if let Some(state) = self.mouse_state.devices.get(&device_id) { // Mouse Drag for (button, handler) in state { @@ -505,11 +583,12 @@ where fn cursor_left( &mut self, - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, kludgine: &mut Kludgine, _device_id: DeviceId, ) { if self.mouse_state.widget.take().is_some() { + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); let mut context = EventContext::new( WidgetContext::new(self.root.clone(), &self.redraw_status, &mut window), kludgine, @@ -520,12 +599,13 @@ where fn mouse_input( &mut self, - mut window: RunningWindow<'_>, + window: kludgine::app::Window<'_, WindowCommand>, kludgine: &mut Kludgine, device_id: DeviceId, state: ElementState, button: MouseButton, ) { + let mut window = RunningWindow::new(window, &self.focused, &self.occluded); match state { ElementState::Pressed => { EventContext::new( @@ -587,7 +667,7 @@ where fn event( &mut self, - mut window: RunningWindow<'_>, + mut window: kludgine::app::Window<'_, WindowCommand>, _kludgine: &mut Kludgine, event: WindowCommand, ) {