app_name, fullscreen, focused

Finally finished removing WindowAttributes.
This commit is contained in:
Jonathan Johnson 2024-09-09 07:49:21 -07:00
parent 8d653c2d6c
commit 0f866009b8
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
2 changed files with 108 additions and 53 deletions

View file

@ -26,6 +26,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
`KeyboardEvent`. Because of this change, `KeyboardEvent` no longer implements `KeyboardEvent`. Because of this change, `KeyboardEvent` no longer implements
`From<winit::event::KeyEvent>`. Instead, a new api `KeyEvent::from_winit` `From<winit::event::KeyEvent>`. Instead, a new api `KeyEvent::from_winit`
allows constructing from both the modifiers and key event. allows constructing from both the modifiers and key event.
- The type alias `WindowAttributes` has been removed. This type is no longer
used in Cushy's public API.
- `Window::attributes` has been made private. All functionality available from
this field should be available directly via functions on `Window`, and when
possible, the attributes are be able to be dynamically updated as well.
- `Window::focused` now accepts an `IntoValue` implementor instead of
`IntoDynamic`. Additionally, the initial value is now used to control whether
the OS should initially activate the window when it is opened.
### Changed ### Changed
@ -85,6 +93,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Window::cursor_position` - `Window::cursor_position`
- `Window::cursor_visible` - `Window::cursor_visible`
- `Window::decorated` - `Window::decorated`
- `Window::enabled_buttons`
- `Window::icon` - `Window::icon`
- `Window::inner_position` - `Window::inner_position`
- `Window::maximized` - `Window::maximized`

View file

@ -31,8 +31,8 @@ use kludgine::app::winit::event::{
use kludgine::app::winit::keyboard::{ use kludgine::app::winit::keyboard::{
Key, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey, SmolStr, Key, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey, SmolStr,
}; };
use kludgine::app::winit::window::{self, Cursor, Icon, WindowLevel}; use kludgine::app::winit::window::{self, Cursor, Fullscreen, Icon, WindowButtons, WindowLevel};
use kludgine::app::{winit, WindowBehavior as _}; use kludgine::app::{winit, WindowAttributes, WindowBehavior as _};
use kludgine::cosmic_text::{fontdb, Family, FamilyOwned}; use kludgine::cosmic_text::{fontdb, Family, FamilyOwned};
use kludgine::drawing::Drawing; use kludgine::drawing::Drawing;
use kludgine::shapes::Shape; use kludgine::shapes::Shape;
@ -477,19 +477,12 @@ where
} }
} }
/// The attributes of a Cushy window.
pub type WindowAttributes = kludgine::app::WindowAttributes;
/// A Cushy window that is not yet running. /// A Cushy window that is not yet running.
#[must_use] #[must_use]
pub struct Window<Behavior = WidgetInstance> pub struct Window<Behavior = WidgetInstance>
where where
Behavior: WindowBehavior, Behavior: WindowBehavior,
{ {
context: Behavior::Context,
pending: PendingWindow,
/// The attributes of this window.
pub attributes: WindowAttributes,
/// The title to display in the title bar of the window. /// The title to display in the title bar of the window.
pub title: Value<String>, pub title: Value<String>,
/// The colors to use to theme the user interface. /// The colors to use to theme the user interface.
@ -527,6 +520,9 @@ where
/// Resizes the window to fit the contents if true. /// Resizes the window to fit the contents if true.
pub resize_to_fit: Value<bool>, pub resize_to_fit: Value<bool>,
context: Behavior::Context,
pending: PendingWindow,
attributes: WindowAttributes,
on_closed: Option<OnceCallback>, on_closed: Option<OnceCallback>,
on_open: Option<OnceCallback<WindowHandle>>, on_open: Option<OnceCallback<WindowHandle>>,
inner_size: Option<Dynamic<Size<UPx>>>, inner_size: Option<Dynamic<Size<UPx>>>,
@ -551,6 +547,8 @@ where
close_requested: Option<Callback<(), bool>>, close_requested: Option<Callback<(), bool>>,
icon: Option<Value<Option<RgbaImage>>>, icon: Option<Value<Option<RgbaImage>>>,
modifiers: Option<Dynamic<Modifiers>>, modifiers: Option<Dynamic<Modifiers>>,
enabled_buttons: Option<Value<WindowButtons>>,
fullscreen: Option<Value<Option<Fullscreen>>>,
} }
impl<Behavior> Default for Window<Behavior> impl<Behavior> Default for Window<Behavior>
@ -644,6 +642,8 @@ where
outer_position: None, outer_position: None,
icon: None, icon: None,
modifiers: None, modifiers: None,
enabled_buttons: None,
fullscreen: None,
} }
} }
@ -713,12 +713,17 @@ where
/// When the window is focused for user input, the dynamic will contain /// When the window is focused for user input, the dynamic will contain
/// `true`. /// `true`.
/// ///
/// `focused` will be initialized with an initial state /// The current value of `focused` will inform the OS whether the window
/// of `false`. /// should be activated upon opening. To prevent state mismatches, if
pub fn focused(mut self, focused: impl IntoDynamic<bool>) -> Self { /// `focused` is a dynamic, it will be initialized with `false` so that the
let focused = focused.into_dynamic(); /// transition to focused can be observed.
focused.set(false); pub fn focused(mut self, focused: impl IntoValue<bool>) -> Self {
self.focused = Some(focused); let focused = focused.into_value();
self.attributes.active = focused.get();
if let Value::Dynamic(focused) = focused {
focused.set(false);
self.focused = Some(focused);
}
self self
} }
@ -737,6 +742,14 @@ where
self self
} }
/// Sets the full screen mode for this window.
pub fn fullscreen(mut self, fullscreen: impl IntoValue<Option<Fullscreen>>) -> Self {
let fullscreen = fullscreen.into_value();
self.attributes.fullscreen = fullscreen.get();
self.fullscreen = Some(fullscreen);
self
}
/// Sets `inner_size` to be the dynamic synchronized with this window's /// Sets `inner_size` to be the dynamic synchronized with this window's
/// inner size. /// inner size.
/// ///
@ -848,6 +861,14 @@ where
self self
} }
/// Sets the enabled buttons for this window.
pub fn enabled_buttons(mut self, buttons: impl IntoValue<WindowButtons>) -> Self {
let buttons = buttons.into_value();
self.attributes.enabled_buttons = buttons.get();
self.enabled_buttons = Some(buttons);
self
}
/// Controls the level of this window. /// Controls the level of this window.
pub fn window_level(mut self, window_level: impl IntoValue<WindowLevel>) -> Self { pub fn window_level(mut self, window_level: impl IntoValue<WindowLevel>) -> Self {
let window_level = window_level.into_value(); let window_level = window_level.into_value();
@ -991,6 +1012,16 @@ where
self.modifiers = Some(modifiers.into_dynamic()); self.modifiers = Some(modifiers.into_dynamic());
self self
} }
/// Sets the name of the application.
///
/// - `WM_CLASS` on X11
/// - application ID on wayland
/// - class name on windows
pub fn app_name(mut self, name: String) -> Self {
self.attributes.app_name = Some(name);
self
}
} }
impl<Behavior> Run for Window<Behavior> impl<Behavior> Run for Window<Behavior>
@ -1060,6 +1091,10 @@ where
outer_size: this.outer_size.unwrap_or_default(), outer_size: this.outer_size.unwrap_or_default(),
window_icon: this.icon.unwrap_or_default(), window_icon: this.icon.unwrap_or_default(),
modifiers: this.modifiers.unwrap_or_default(), modifiers: this.modifiers.unwrap_or_default(),
enabled_buttons: this
.enabled_buttons
.unwrap_or(Value::Constant(WindowButtons::all())),
fullscreen: this.fullscreen.unwrap_or_default(),
}), }),
pending: this.pending, pending: this.pending,
}, },
@ -1217,6 +1252,8 @@ struct OpenWindow<T> {
outer_position: Tracked<Dynamic<Point<Px>>>, outer_position: Tracked<Dynamic<Point<Px>>>,
inner_position: Dynamic<Point<Px>>, inner_position: Dynamic<Point<Px>>,
window_icon: Tracked<Value<Option<RgbaImage>>>, window_icon: Tracked<Value<Option<RgbaImage>>>,
enabled_buttons: Tracked<Value<WindowButtons>>,
fullscreen: Tracked<Value<Option<Fullscreen>>>,
modifiers: Dynamic<Modifiers>, modifiers: Dynamic<Modifiers>,
} }
@ -1655,6 +1692,8 @@ where
outer_position: Tracked::from(settings.outer_position).ignoring_first(), outer_position: Tracked::from(settings.outer_position).ignoring_first(),
window_icon: Tracked::from(settings.window_icon), window_icon: Tracked::from(settings.window_icon),
modifiers: settings.modifiers, modifiers: settings.modifiers,
enabled_buttons: Tracked::from(settings.enabled_buttons).ignoring_first(),
fullscreen: Tracked::from(settings.fullscreen).ignoring_first(),
}; };
this.synchronize_platform_window(&mut window); this.synchronize_platform_window(&mut window);
@ -1843,39 +1882,39 @@ where
where where
W: PlatformWindowImplementation, W: PlatformWindowImplementation,
{ {
macro_rules! when_updated {
($prop:ident, $handle:ident, $block:expr) => {
self.$prop.inner_sync_when_changed($handle.clone());
if let Some($prop) = self.$prop.updated() {
$block
}
};
}
self.redraw_status.sync_received(); self.redraw_status.sync_received();
self.update_ized(window); self.update_ized(window);
if let Some(winit) = window.winit() { if let Some(winit) = window.winit() {
let mut redraw = false; let mut redraw = false;
let handle = window.handle(self.redraw_status.clone()); let handle = window.handle(self.redraw_status.clone());
self.outer_position.inner_sync_when_changed(handle.clone());
if let Some(position) = self.outer_position.updated() { when_updated!(outer_position, handle, {
winit.set_outer_position(PhysicalPosition::<i32>::from(*position)); winit.set_outer_position(PhysicalPosition::<i32>::from(*outer_position));
} });
self.content_protected when_updated!(content_protected, handle, {
.inner_sync_when_changed(handle.clone()); winit.set_content_protected(*content_protected);
if let Some(protected) = self.content_protected.updated() { });
winit.set_content_protected(*protected); when_updated!(cursor_hittest, handle, {
} let _ = winit.set_cursor_hittest(*cursor_hittest);
self.cursor_hittest.inner_sync_when_changed(handle.clone()); });
if let Some(hit) = self.cursor_hittest.updated() { when_updated!(cursor_visible, handle, {
let _ = winit.set_cursor_hittest(*hit); winit.set_cursor_visible(*cursor_visible);
} });
self.cursor_visible.inner_sync_when_changed(handle.clone()); when_updated!(window_level, handle, {
if let Some(visible) = self.cursor_visible.updated() {
winit.set_cursor_visible(*visible);
}
self.window_level.inner_sync_when_changed(handle.clone());
if let Some(window_level) = self.window_level.updated() {
winit.set_window_level(*window_level); winit.set_window_level(*window_level);
} });
self.decorated.inner_sync_when_changed(handle.clone()); when_updated!(decorated, handle, {
if let Some(decorated) = self.decorated.updated() {
winit.set_decorations(*decorated); winit.set_decorations(*decorated);
} });
self.resize_increments when_updated!(resize_increments, handle, {
.inner_sync_when_changed(handle.clone());
if let Some(resize_increments) = self.resize_increments.updated() {
let increments: Option<PhysicalSize<f32>> = let increments: Option<PhysicalSize<f32>> =
if resize_increments.width > 0 || resize_increments.height > 0 { if resize_increments.width > 0 || resize_increments.height > 0 {
Some(PhysicalSize::new( Some(PhysicalSize::new(
@ -1886,24 +1925,27 @@ where
None None
}; };
winit.set_resize_increments(increments); winit.set_resize_increments(increments);
} });
self.visible.inner_sync_when_changed(handle.clone()); when_updated!(visible, handle, {
if let Some(visible) = self.visible.updated() {
winit.set_visible(*visible); winit.set_visible(*visible);
} });
self.resizable.inner_sync_when_changed(handle.clone()); when_updated!(resizable, handle, {
if let Some(resizable) = self.resizable.updated() {
winit.set_resizable(*resizable); winit.set_resizable(*resizable);
redraw = true; redraw = true;
} });
self.window_icon.inner_sync_when_changed(handle.clone()); when_updated!(window_icon, handle, {
if let Some(icon) = self.window_icon.updated() { let icon = window_icon.as_ref().map(|icon| {
let icon = icon.as_ref().map(|icon| {
Icon::from_rgba(icon.as_raw().clone(), icon.width(), icon.height()) Icon::from_rgba(icon.as_raw().clone(), icon.width(), icon.height())
.expect("valid image") .expect("valid image")
}); });
winit.set_window_icon(icon); winit.set_window_icon(icon);
} });
when_updated!(enabled_buttons, handle, {
winit.set_enabled_buttons(*enabled_buttons);
});
when_updated!(fullscreen, handle, {
winit.set_fullscreen(fullscreen.clone());
});
if redraw { if redraw {
window.set_needs_redraw(); window.set_needs_redraw();
@ -2687,7 +2729,7 @@ pub(crate) mod sealed {
use figures::{Fraction, Point, Size}; use figures::{Fraction, Point, Size};
use image::{DynamicImage, RgbaImage}; use image::{DynamicImage, RgbaImage};
use kludgine::app::winit::event::Modifiers; use kludgine::app::winit::event::Modifiers;
use kludgine::app::winit::window::{UserAttentionType, WindowLevel}; use kludgine::app::winit::window::{Fullscreen, UserAttentionType, WindowButtons, WindowLevel};
use kludgine::Color; use kludgine::Color;
use parking_lot::Mutex; use parking_lot::Mutex;
@ -2746,6 +2788,8 @@ pub(crate) mod sealed {
pub outer_size: Dynamic<Size<UPx>>, pub outer_size: Dynamic<Size<UPx>>,
pub window_icon: Value<Option<RgbaImage>>, pub window_icon: Value<Option<RgbaImage>>,
pub modifiers: Dynamic<Modifiers>, pub modifiers: Dynamic<Modifiers>,
pub enabled_buttons: Value<WindowButtons>,
pub fullscreen: Value<Option<Fullscreen>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -3359,6 +3403,8 @@ impl StandaloneWindowBuilder {
outer_size: Dynamic::default(), outer_size: Dynamic::default(),
window_icon: Value::Constant(None), window_icon: Value::Constant(None),
modifiers: Dynamic::default(), modifiers: Dynamic::default(),
enabled_buttons: Value::dynamic(WindowButtons::all()),
fullscreen: Value::default(),
}, },
); );