Merged internal and public WindowHandle types

This commit is contained in:
Jonathan Johnson 2023-12-27 09:04:29 -08:00
parent aa47539518
commit 76528ee374
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
5 changed files with 139 additions and 118 deletions

View file

@ -15,7 +15,6 @@ use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine};
use crate::animation::ZeroToOne;
use crate::context::sealed::WindowHandle;
use crate::graphics::Graphics;
use crate::styles::components::{
CornerRadius, FontFamily, FontStyle, FontWeight, HighlightColor, LayoutOrder, LineHeight,
@ -850,7 +849,6 @@ impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '
pub struct WidgetContext<'context, 'window> {
current_node: MountedWidget,
pub(crate) tree: Tree,
redraw_status: &'context InvalidationStatus,
window: &'context mut RunningWindow<'window>,
theme: Cow<'context, ThemePair>,
cursor: &'context mut CursorState,
@ -862,16 +860,12 @@ pub struct WidgetContext<'context, 'window> {
impl<'context, 'window> WidgetContext<'context, 'window> {
pub(crate) fn new(
current_node: MountedWidget,
redraw_status: &'context InvalidationStatus,
theme: &'context ThemePair,
window: &'context mut RunningWindow<'window>,
theme_mode: ThemeMode,
cursor: &'context mut CursorState,
) -> Self {
let enabled = current_node.enabled(&WindowHandle {
kludgine: window.handle(),
redraw_status: redraw_status.clone(),
});
let enabled = current_node.enabled(&window.handle());
let tree = current_node.tree();
Self {
pending_state: PendingState::Owned(PendingWidgetState {
@ -891,7 +885,6 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
},
cursor,
current_node,
redraw_status,
theme: Cow::Borrowed(theme),
window,
}
@ -902,7 +895,6 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
WidgetContext {
tree: self.tree.clone(),
current_node: self.current_node.clone(),
redraw_status: self.redraw_status,
window: &mut *self.window,
theme: Cow::Borrowed(self.theme.as_ref()),
pending_state: self.pending_state.borrowed(),
@ -941,7 +933,6 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
},
current_node,
tree: self.tree.clone(),
redraw_status: self.redraw_status,
window: &mut *self.window,
theme,
pending_state: self.pending_state.borrowed(),
@ -1157,13 +1148,6 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
self.effective_styles.try_get(query, self)
}
pub(crate) fn handle(&self) -> WindowHandle {
WindowHandle {
kludgine: self.window.handle(),
redraw_status: self.redraw_status.clone(),
}
}
/// Returns the window containing this widget.
#[must_use]
pub fn window(&self) -> &RunningWindow<'window> {
@ -1297,6 +1281,19 @@ impl InvalidationStatus {
}
}
impl Eq for InvalidationStatus {}
impl PartialEq for InvalidationStatus {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.invalidated, &other.invalidated)
}
}
impl std::hash::Hash for InvalidationStatus {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.invalidated).hash(state);
}
}
/// A type chat can convert to a [`MountedWidget`] through a [`WidgetContext`].
pub trait ManageWidget {
/// The managed type, which can be `Option<MountedWidget>` or
@ -1392,52 +1389,11 @@ pub trait Trackable: sealed::Trackable {}
impl<T> Trackable for T where T: sealed::Trackable {}
pub(crate) mod sealed {
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::context::InvalidationStatus;
use crate::widget::WidgetId;
use crate::window::sealed::WindowCommand;
use crate::window::WindowHandle;
pub trait Trackable {
fn redraw_when_changed(&self, handle: WindowHandle);
fn invalidate_when_changed(&self, handle: WindowHandle, id: WidgetId);
}
#[derive(Clone)]
pub struct WindowHandle {
pub(crate) kludgine: kludgine::app::WindowHandle<WindowCommand>,
pub(crate) redraw_status: InvalidationStatus,
}
impl Eq for WindowHandle {}
impl PartialEq for WindowHandle {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(
&self.redraw_status.invalidated,
&other.redraw_status.invalidated,
)
}
}
impl Hash for WindowHandle {
fn hash<H: Hasher>(&self, state: &mut H) {
Arc::as_ptr(&self.redraw_status.invalidated).hash(state);
}
}
impl WindowHandle {
pub fn redraw(&self) {
if self.redraw_status.should_send_refresh() {
let _result = self.kludgine.send(WindowCommand::Redraw);
}
}
pub fn invalidate(&self, widget: WidgetId) {
if self.redraw_status.invalidate(widget) {
self.redraw();
}
}
}
}

View file

@ -6,12 +6,11 @@ use alot::{LotId, Lots};
use kludgine::figures::units::{Px, UPx};
use kludgine::figures::{Point, Rect, Size};
use crate::context::sealed::WindowHandle;
use crate::styles::{Styles, ThemePair, VisualOrder};
use crate::utils::IgnorePoison;
use crate::value::Value;
use crate::widget::{MountedWidget, WidgetId, WidgetInstance};
use crate::window::ThemeMode;
use crate::window::{ThemeMode, WindowHandle};
use crate::ConstraintLimit;
#[derive(Clone, Default)]

View file

@ -17,11 +17,11 @@ use intentional::Assert;
use kempt::{Map, Sort};
use crate::animation::{AnimationHandle, DynamicTransition, IntoAnimate, LinearInterpolate, Spawn};
use crate::context::sealed::WindowHandle;
use crate::context::{self, WidgetContext};
use crate::utils::{run_in_bg, IgnorePoison, WithClone};
use crate::widget::{Children, MakeWidget, MakeWidgetWithTag, WidgetId, WidgetInstance};
use crate::widgets::{Radio, Select, Space, Switcher};
use crate::window::WindowHandle;
/// An instance of a value that provides APIs to observe and react to its
/// contents.

View file

@ -19,7 +19,6 @@ use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, Size};
use kludgine::Color;
use crate::app::{Application, Open, PendingApp, Run};
use crate::context::sealed::WindowHandle;
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
use crate::styles::components::{
FontFamily, FontStyle, FontWeight, Heading1FontFamily, Heading1Style, Heading1Weight,
@ -43,7 +42,7 @@ use crate::widgets::{
Align, Button, Checkbox, Collapse, Container, Expand, Layers, Resize, Scroll, Space, Stack,
Style, Themed, ThemedMode, Validated, Wrap,
};
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior};
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior, WindowHandle};
use crate::ConstraintLimit;
/// A type that makes up a graphical user interface.

View file

@ -2,6 +2,7 @@
use std::cell::RefCell;
use std::ffi::OsStr;
use std::hash::Hash;
use std::ops::{Deref, DerefMut, Not};
use std::path::Path;
use std::string::ToString;
@ -17,7 +18,7 @@ use kludgine::app::winit::event::{
use kludgine::app::winit::keyboard::{Key, NamedKey};
use kludgine::app::winit::window;
use kludgine::app::WindowBehavior as _;
use kludgine::cosmic_text::{Family, FamilyOwned};
use kludgine::cosmic_text::{fontdb, Family, FamilyOwned};
use kludgine::figures::units::{Px, UPx};
use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, ScreenScale, Size, Zero};
use kludgine::render::Drawing;
@ -46,6 +47,7 @@ use crate::{initialize_tracing, ConstraintLimit};
/// A currently running Gooey window.
pub struct RunningWindow<'window> {
window: kludgine::app::Window<'window, WindowCommand>,
invalidation_status: InvalidationStatus,
gooey: Gooey,
focused: Dynamic<bool>,
occluded: Dynamic<bool>,
@ -55,6 +57,7 @@ pub struct RunningWindow<'window> {
impl<'window> RunningWindow<'window> {
pub(crate) fn new(
window: kludgine::app::Window<'window, WindowCommand>,
invalidation_status: &InvalidationStatus,
gooey: &Gooey,
focused: &Dynamic<bool>,
occluded: &Dynamic<bool>,
@ -62,6 +65,7 @@ impl<'window> RunningWindow<'window> {
) -> Self {
Self {
window,
invalidation_status: invalidation_status.clone(),
gooey: gooey.clone(),
focused: focused.clone(),
occluded: occluded.clone(),
@ -83,6 +87,20 @@ impl<'window> RunningWindow<'window> {
&self.occluded
}
/// Request that the window closes.
///
/// A window may disallow itself from being closed by customizing
/// [`WindowBehavior::close_requested`].
pub fn request_close(&self) {
self.handle().request_close();
}
/// Returns a handle to this window.
#[must_use]
pub fn handle(&self) -> WindowHandle {
WindowHandle::new(self.window.handle(), self.invalidation_status.clone())
}
/// Returns a dynamic that is synchronized with this window's inner size.
///
/// Whenever the window is resized, this dynamic will be updated with the
@ -332,12 +350,14 @@ where
App: Application,
{
let gooey = app.gooey().clone();
let redraw_status = InvalidationStatus::default();
let handle = GooeyWindow::<Behavior>::open_with(
app,
sealed::Context {
user: self.context,
settings: RefCell::new(sealed::WindowSettings {
gooey,
redraw_status: redraw_status.clone(),
on_closed: self.on_closed,
transparent: self.attributes.transparent,
attributes: Some(self.attributes),
@ -356,7 +376,7 @@ where
},
)?;
Ok(handle.map(WindowHandle::from))
Ok(handle.map(|handle| WindowHandle::new(handle, redraw_status)))
}
fn run_in(self, app: PendingApp) -> crate::Result {
@ -455,7 +475,6 @@ where
EventContext::new(
WidgetContext::new(
previously_active,
&self.redraw_status,
&self.current_theme,
window,
self.theme_mode.get(),
@ -468,7 +487,6 @@ where
EventContext::new(
WidgetContext::new(
default.clone(),
&self.redraw_status,
&self.current_theme,
window,
self.theme_mode.get(),
@ -487,7 +505,6 @@ where
EventContext::new(
WidgetContext::new(
keyboard_activated,
&self.redraw_status,
&self.current_theme,
window,
self.theme_mode.get(),
@ -517,7 +534,6 @@ where
let mut context = EventContext::new(
WidgetContext::new(
managed,
&self.redraw_status,
&self.current_theme,
window,
self.theme_mode.get(),
@ -591,37 +607,11 @@ where
root_mode.unwrap_or(RootMode::Fit)
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
enum RootMode {
Fit,
Expand,
Align,
}
impl<T> kludgine::app::WindowBehavior<WindowCommand> for GooeyWindow<T>
where
T: WindowBehavior,
{
type Context = sealed::Context<T::Context>;
fn initialize(
window: kludgine::app::Window<'_, WindowCommand>,
graphics: &mut kludgine::Graphics<'_>,
context: Self::Context,
) -> Self {
let mut settings = context.settings.borrow_mut();
let gooey = settings.gooey.clone();
let occluded = settings.occluded.take().unwrap_or_default();
let focused = settings.focused.take().unwrap_or_default();
let theme = settings.theme.take().expect("theme always present");
let inner_size = settings.inner_size.take().unwrap_or_default();
let on_closed = settings.on_closed.take();
inner_size.set(window.inner_size());
let fontdb = graphics.font_system().db_mut();
fn load_fonts(
settings: &mut sealed::WindowSettings,
fontdb: &mut fontdb::Database,
) -> FontState {
for font_to_load in settings.font_data_to_load.drain(..) {
fontdb.load_font_data(font_to_load);
}
@ -668,6 +658,39 @@ where
|| default_family(Family::Cursive),
|name| fontdb.set_cursive_family(name),
);
fonts
}
}
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
enum RootMode {
Fit,
Expand,
Align,
}
impl<T> kludgine::app::WindowBehavior<WindowCommand> for GooeyWindow<T>
where
T: WindowBehavior,
{
type Context = sealed::Context<T::Context>;
fn initialize(
window: kludgine::app::Window<'_, WindowCommand>,
graphics: &mut kludgine::Graphics<'_>,
context: Self::Context,
) -> Self {
let mut settings = context.settings.borrow_mut();
let gooey = settings.gooey.clone();
let occluded = settings.occluded.take().unwrap_or_default();
let focused = settings.focused.take().unwrap_or_default();
let theme = settings.theme.take().expect("theme always present");
let inner_size = settings.inner_size.take().unwrap_or_default();
let on_closed = settings.on_closed.take();
inner_size.set(window.inner_size());
let fonts = Self::load_fonts(&mut settings, graphics.font_system().db_mut());
let theme_mode = match settings.theme_mode.take() {
Some(Value::Dynamic(dynamic)) => {
@ -678,8 +701,16 @@ where
None => Value::dynamic(window.theme().into()),
};
let transparent = settings.transparent;
let redraw_status = settings.redraw_status.clone();
let mut behavior = T::initialize(
&mut RunningWindow::new(window, &gooey, &focused, &occluded, &inner_size),
&mut RunningWindow::new(
window,
&redraw_status,
&gooey,
&focused,
&occluded,
&inner_size,
),
context.user,
);
let tree = Tree::default();
@ -701,7 +732,7 @@ where
widget: None,
},
mouse_buttons: AHashMap::default(),
redraw_status: InvalidationStatus::default(),
redraw_status,
initial_frame: true,
occluded,
focused,
@ -740,6 +771,7 @@ where
let resizable = window.winit().is_resizable();
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -752,7 +784,6 @@ where
let mut context = GraphicsContext {
widget: WidgetContext::new(
self.root.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -876,6 +907,7 @@ where
&mut self.behavior,
&mut RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -949,6 +981,7 @@ where
};
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -957,7 +990,6 @@ where
let mut target = EventContext::new(
WidgetContext::new(
target,
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -994,7 +1026,6 @@ where
let mut target = EventContext::new(
WidgetContext::new(
target,
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1055,6 +1086,7 @@ where
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1063,7 +1095,6 @@ where
let mut widget = EventContext::new(
WidgetContext::new(
widget,
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1091,6 +1122,7 @@ where
.unwrap_or_else(|| self.tree.widget(self.root.id()).expect("missing widget"));
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1099,7 +1131,6 @@ where
let mut target = EventContext::new(
WidgetContext::new(
widget,
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1124,6 +1155,7 @@ where
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1133,7 +1165,6 @@ where
EventContext::new(
WidgetContext::new(
self.root.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1152,7 +1183,6 @@ where
let mut context = EventContext::new(
WidgetContext::new(
handler.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1177,6 +1207,7 @@ where
if self.cursor.widget.take().is_some() {
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1185,7 +1216,6 @@ where
let mut context = EventContext::new(
WidgetContext::new(
self.root.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1207,6 +1237,7 @@ where
) {
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1217,7 +1248,6 @@ where
EventContext::new(
WidgetContext::new(
self.root.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1236,7 +1266,6 @@ where
&mut EventContext::new(
WidgetContext::new(
hovered.clone(),
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1276,7 +1305,6 @@ where
let mut context = EventContext::new(
WidgetContext::new(
handler,
&self.redraw_status,
&self.current_theme,
&mut window,
self.theme_mode.get(),
@ -1321,6 +1349,7 @@ where
WindowCommand::RequestClose => {
let mut window = RunningWindow::new(
window,
&self.redraw_status,
&self.gooey,
&self.focused,
&self.occluded,
@ -1367,6 +1396,7 @@ pub(crate) mod sealed {
use kludgine::figures::Size;
use crate::app::Gooey;
use crate::context::InvalidationStatus;
use crate::styles::{FontFamilyList, ThemePair};
use crate::value::{Dynamic, Value};
use crate::widget::OnceCallback;
@ -1379,6 +1409,7 @@ pub(crate) mod sealed {
pub struct WindowSettings {
pub gooey: Gooey,
pub redraw_status: InvalidationStatus,
pub attributes: Option<WindowAttributes>,
pub occluded: Option<Dynamic<bool>>,
pub focused: Option<Dynamic<bool>>,
@ -1501,20 +1532,56 @@ fn default_family(query: Family<'_>) -> Option<FamilyOwned> {
}
/// A handle to an open Gooey window.
pub struct WindowHandle(kludgine::app::WindowHandle<sealed::WindowCommand>);
#[derive(Clone)]
pub struct WindowHandle {
pub(crate) kludgine: kludgine::app::WindowHandle<WindowCommand>,
pub(crate) redraw_status: InvalidationStatus,
}
impl WindowHandle {
pub(crate) fn new(
kludgine: kludgine::app::WindowHandle<WindowCommand>,
redraw_status: InvalidationStatus,
) -> Self {
Self {
kludgine,
redraw_status,
}
}
/// Request that the window closes.
///
/// A window may disallow itself from being closed by customizing
/// [`WindowBehavior::close_requested`].
pub fn request_close(&self) {
let _result = self.0.send(sealed::WindowCommand::RequestClose);
let _result = self.kludgine.send(sealed::WindowCommand::RequestClose);
}
/// Requests that the window redraws.
pub fn redraw(&self) {
if self.redraw_status.should_send_refresh() {
let _result = self.kludgine.send(WindowCommand::Redraw);
}
}
/// Marks `widget` as invalidated, and if needed, refreshes the window.
pub fn invalidate(&self, widget: WidgetId) {
if self.redraw_status.invalidate(widget) {
self.redraw();
}
}
}
impl From<kludgine::app::WindowHandle<sealed::WindowCommand>> for WindowHandle {
fn from(handle: kludgine::app::WindowHandle<sealed::WindowCommand>) -> Self {
WindowHandle(handle)
impl Eq for WindowHandle {}
impl PartialEq for WindowHandle {
fn eq(&self, other: &Self) -> bool {
self.redraw_status == other.redraw_status
}
}
impl Hash for WindowHandle {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.redraw_status.hash(state);
}
}