mirror of
https://github.com/danbulant/cushy
synced 2026-06-19 14:31:04 +00:00
parent
9304165981
commit
634a4b7af8
4 changed files with 702 additions and 69 deletions
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- `PendingWindow`s opened into a `PendingApp` now have working `WindowHandle`s.
|
||||
As a result of this fix, `Open::open()` now returns a `WindowHandle` instead
|
||||
of an `Option<WindowHandle>`.
|
||||
- `CushyWindow::set_occluded` and `CushyWindow::resize now require a
|
||||
`PlatformWindowImplementation` parameter.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
|
@ -51,6 +53,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- `Window::resize_to_fit`
|
||||
- `StandaloneWindowBuilder::resize_to_fit`
|
||||
- `Tracked<Source>` is a new type that can store a `Dynamic<T>` or `Value<T>`
|
||||
and provide easy ways to track whether the value has been updated.
|
||||
- Many new functions have been added to `Window` to expose more functionality
|
||||
supported by winit:
|
||||
|
||||
- `Window::content_protected`
|
||||
- `Window::cursor_hittest`
|
||||
- `Window::cursor_visible`
|
||||
- `Window::cursor_position`
|
||||
- `Window::decorated`
|
||||
- `Window::window_level`
|
||||
- `Window::minimized`
|
||||
- `Window::maximized`
|
||||
- `Window::resized`
|
||||
- `Window::resize_increments`
|
||||
- `Window::transparent`
|
||||
- `Window::visible`
|
||||
|
||||
[139]: https://github.com/khonsulabs/cushy/issues/139
|
||||
|
||||
|
|
|
|||
|
|
@ -1425,12 +1425,14 @@ pub(crate) mod sealed {
|
|||
|
||||
pub trait Trackable {
|
||||
fn inner_redraw_when_changed(&self, handle: WindowHandle);
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle);
|
||||
fn inner_invalidate_when_changed(&self, handle: WindowHandle, id: WidgetId);
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct InvalidationStatus {
|
||||
refresh_sent: Arc<AtomicBool>,
|
||||
sync_sent: Arc<AtomicBool>,
|
||||
invalidated: Arc<Mutex<Set<WidgetId>>>,
|
||||
}
|
||||
|
||||
|
|
@ -1441,10 +1443,20 @@ pub(crate) mod sealed {
|
|||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn should_send_sync(&self) -> bool {
|
||||
self.sync_sent
|
||||
.compare_exchange(false, true, Ordering::Release, Ordering::Acquire)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn refresh_received(&self) {
|
||||
self.refresh_sent.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn sync_received(&self) {
|
||||
self.sync_sent.store(false, Ordering::Release);
|
||||
}
|
||||
|
||||
pub fn invalidate(&self, widget: WidgetId) -> bool {
|
||||
let mut invalidated = self.invalidated.lock();
|
||||
invalidated.insert(widget)
|
||||
|
|
|
|||
277
src/value.rs
277
src/value.rs
|
|
@ -12,7 +12,7 @@ use std::task::{Poll, Waker};
|
|||
use std::thread::{self, ThreadId};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use ahash::AHashSet;
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use alot::{LotId, Lots};
|
||||
use intentional::Assert;
|
||||
use kempt::{Map, Sort};
|
||||
|
|
@ -1352,6 +1352,10 @@ impl<T> context::sealed::Trackable for Dynamic<T> {
|
|||
self.0.redraw_when_changed(handle);
|
||||
}
|
||||
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle) {
|
||||
self.0.sync_when_changed(handle);
|
||||
}
|
||||
|
||||
fn inner_invalidate_when_changed(&self, handle: WindowHandle, id: WidgetId) {
|
||||
self.0.invalidate_when_changed(handle, id);
|
||||
}
|
||||
|
|
@ -1509,7 +1513,12 @@ impl<T> DynamicData<T> {
|
|||
|
||||
pub fn redraw_when_changed(&self, window: WindowHandle) {
|
||||
let mut state = self.state().expect("deadlocked");
|
||||
state.invalidation.windows.insert(window);
|
||||
state.invalidation.windows.insert(window, true);
|
||||
}
|
||||
|
||||
pub fn sync_when_changed(&self, window: WindowHandle) {
|
||||
let mut state = self.state().expect("deadlocked");
|
||||
state.invalidation.windows.entry(window).or_insert(false);
|
||||
}
|
||||
|
||||
pub fn invalidate_when_changed(&self, window: WindowHandle, widget: WidgetId) {
|
||||
|
|
@ -1775,7 +1784,7 @@ impl AddAssign for CallbackHandle {
|
|||
|
||||
#[derive(Default)]
|
||||
struct InvalidationState {
|
||||
windows: AHashSet<WindowHandle>,
|
||||
windows: AHashMap<WindowHandle, bool>,
|
||||
widgets: AHashSet<(WindowHandle, WidgetId)>,
|
||||
wakers: Vec<Waker>,
|
||||
}
|
||||
|
|
@ -1785,8 +1794,12 @@ impl InvalidationState {
|
|||
for (window, widget) in self.widgets.drain() {
|
||||
window.invalidate(widget);
|
||||
}
|
||||
for window in self.windows.drain() {
|
||||
window.redraw();
|
||||
for (window, redraw) in self.windows.drain() {
|
||||
if redraw {
|
||||
window.redraw();
|
||||
} else {
|
||||
window.sync();
|
||||
}
|
||||
}
|
||||
for waker in self.wakers.drain(..) {
|
||||
waker.wake();
|
||||
|
|
@ -1827,7 +1840,7 @@ impl<T> State<T> {
|
|||
},
|
||||
callbacks: Arc::default(),
|
||||
invalidation: InvalidationState {
|
||||
windows: AHashSet::new(),
|
||||
windows: AHashMap::new(),
|
||||
wakers: Vec::new(),
|
||||
widgets: AHashSet::new(),
|
||||
},
|
||||
|
|
@ -2341,6 +2354,10 @@ impl<T> context::sealed::Trackable for DynamicReader<T> {
|
|||
self.source.redraw_when_changed(handle);
|
||||
}
|
||||
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle) {
|
||||
self.source.sync_when_changed(handle);
|
||||
}
|
||||
|
||||
fn inner_invalidate_when_changed(&self, handle: WindowHandle, id: WidgetId) {
|
||||
self.source.invalidate_when_changed(handle, id);
|
||||
}
|
||||
|
|
@ -2858,6 +2875,12 @@ impl<T> crate::context::sealed::Trackable for ReadOnly<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle) {
|
||||
if let ReadOnly::Reader(dynamic) = self {
|
||||
dynamic.inner_sync_when_changed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_redraw_when_changed(&self, handle: WindowHandle) {
|
||||
if let ReadOnly::Reader(dynamic) = self {
|
||||
dynamic.inner_redraw_when_changed(handle);
|
||||
|
|
@ -2872,6 +2895,12 @@ impl<T> crate::context::sealed::Trackable for Value<T> {
|
|||
}
|
||||
}
|
||||
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle) {
|
||||
if let Value::Dynamic(dynamic) = self {
|
||||
dynamic.inner_sync_when_changed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_redraw_when_changed(&self, handle: WindowHandle) {
|
||||
if let Value::Dynamic(dynamic) = self {
|
||||
dynamic.inner_redraw_when_changed(handle);
|
||||
|
|
@ -3840,6 +3869,242 @@ impl Watcher {
|
|||
}
|
||||
}
|
||||
|
||||
/// A value that has its read and updated states tracked.
|
||||
pub struct Tracked<Source>
|
||||
where
|
||||
Source: TrackedSource,
|
||||
{
|
||||
current: Source::Value,
|
||||
source: Source,
|
||||
current_generation: Generation,
|
||||
unread: bool,
|
||||
}
|
||||
|
||||
impl<Source> Tracked<Source>
|
||||
where
|
||||
Source: TrackedSource,
|
||||
{
|
||||
/// Returns a new tracked instance.
|
||||
pub fn new(source: Source) -> Self {
|
||||
let (current, current_generation) = source.read();
|
||||
Self {
|
||||
current,
|
||||
current_generation,
|
||||
source,
|
||||
unread: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates this tracked instance's cached value from the source.
|
||||
///
|
||||
/// Returns true if a value hasn't been read yet.
|
||||
pub fn update(&mut self) -> bool {
|
||||
if let Some((updated, updated_generation)) =
|
||||
self.source.read_if_needed(self.current_generation)
|
||||
{
|
||||
self.current = updated;
|
||||
self.current_generation = updated_generation;
|
||||
self.unread = true;
|
||||
true
|
||||
} else {
|
||||
self.unread
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current value from the source, if it hasn't been read
|
||||
/// before.
|
||||
pub fn updated(&mut self) -> Option<&Source::Value> {
|
||||
if self.update() {
|
||||
self.unread = false;
|
||||
Some(&self.current)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the currently cached value hasn't been read.
|
||||
pub const fn unread(&self) -> bool {
|
||||
self.unread
|
||||
}
|
||||
|
||||
/// Reads the current value from the source.
|
||||
pub fn read(&mut self) -> &Source::Value {
|
||||
self.update();
|
||||
self.read_cached()
|
||||
}
|
||||
|
||||
/// Reads the current cached value.
|
||||
///
|
||||
/// This function does not check if an updated value exists in the source.
|
||||
pub fn read_cached(&mut self) -> &Source::Value {
|
||||
self.unread = false;
|
||||
self.peek()
|
||||
}
|
||||
|
||||
/// Returns the current cached value without changing the unread state.
|
||||
pub const fn peek(&self) -> &Source::Value {
|
||||
&self.current
|
||||
}
|
||||
|
||||
/// Returns the source being tracked.
|
||||
pub const fn source(&self) -> &Source {
|
||||
&self.source
|
||||
}
|
||||
|
||||
/// Marks the current value in the source as being read.
|
||||
pub fn mark_read(&mut self) {
|
||||
self.unread = false;
|
||||
self.current_generation = self.source.generation();
|
||||
}
|
||||
|
||||
/// Updates the value stored in the source, and marks it as being read.
|
||||
pub fn set_and_read(&mut self, new_value: Source::Value)
|
||||
where
|
||||
Source::Value: PartialEq + Clone,
|
||||
{
|
||||
self.current = new_value;
|
||||
self.unread = false;
|
||||
if self.source.set(self.current.clone()) {
|
||||
self.current_generation = self.source.generation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Source`] that can be used in a [`Tracked`] instance.
|
||||
pub trait TrackedSource: sealed::TrackedSource {}
|
||||
|
||||
mod sealed {
|
||||
use super::Generation;
|
||||
pub trait TrackedSource {
|
||||
type Value;
|
||||
fn read(&self) -> (Self::Value, Generation);
|
||||
fn read_if_needed(&self, read_generation: Generation) -> Option<(Self::Value, Generation)>;
|
||||
fn generation(&self) -> Generation;
|
||||
fn set(&self, new_value: Self::Value) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TrackedSource for Dynamic<T> where T: Clone + PartialEq {}
|
||||
|
||||
impl<T> sealed::TrackedSource for Dynamic<T>
|
||||
where
|
||||
T: Clone + PartialEq,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn read(&self) -> (Self::Value, Generation) {
|
||||
self.map_generational(|g| (g.clone(), g.generation()))
|
||||
}
|
||||
|
||||
fn read_if_needed(&self, read_generation: Generation) -> Option<(Self::Value, Generation)> {
|
||||
self.map_generational(|g| {
|
||||
if g.generation() == read_generation {
|
||||
None
|
||||
} else {
|
||||
Some((g.clone(), g.generation()))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generation(&self) -> Generation {
|
||||
Source::generation(self)
|
||||
}
|
||||
|
||||
fn set(&self, new_value: Self::Value) -> bool {
|
||||
let mut value = self.lock();
|
||||
if *value == new_value {
|
||||
false
|
||||
} else {
|
||||
*value = new_value;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TrackedSource for Value<T> where T: Clone + PartialEq {}
|
||||
|
||||
impl<T> sealed::TrackedSource for Value<T>
|
||||
where
|
||||
T: Clone + PartialEq,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn read(&self) -> (Self::Value, Generation) {
|
||||
match self {
|
||||
Value::Constant(value) => (value.clone(), Generation::default()),
|
||||
Value::Dynamic(value) => sealed::TrackedSource::read(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_if_needed(&self, read_generation: Generation) -> Option<(Self::Value, Generation)> {
|
||||
match self {
|
||||
Value::Constant(_) => None,
|
||||
Value::Dynamic(value) => sealed::TrackedSource::read_if_needed(value, read_generation),
|
||||
}
|
||||
}
|
||||
|
||||
fn generation(&self) -> Generation {
|
||||
match self {
|
||||
Value::Constant(_) => Generation::default(),
|
||||
Value::Dynamic(value) => Source::generation(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn set(&self, new_value: Self::Value) -> bool {
|
||||
match self {
|
||||
Value::Constant(_) => false,
|
||||
Value::Dynamic(value) => sealed::TrackedSource::set(value, new_value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Destination<S::Value> for Tracked<S>
|
||||
where
|
||||
S: TrackedSource + Destination<S::Value>,
|
||||
{
|
||||
fn try_map_mut<R>(
|
||||
&self,
|
||||
map: impl FnOnce(Mutable<'_, S::Value>) -> R,
|
||||
) -> Result<R, DeadlockError> {
|
||||
self.source.try_map_mut(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Dynamic<T>> for Tracked<Dynamic<T>>
|
||||
where
|
||||
T: Clone + PartialEq,
|
||||
{
|
||||
fn from(source: Dynamic<T>) -> Self {
|
||||
Self::new(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Value<T>> for Tracked<Value<T>>
|
||||
where
|
||||
T: Clone + PartialEq,
|
||||
{
|
||||
fn from(source: Value<T>) -> Self {
|
||||
Self::new(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Source> context::sealed::Trackable for Tracked<Source>
|
||||
where
|
||||
Source: Trackable + TrackedSource,
|
||||
{
|
||||
fn inner_invalidate_when_changed(&self, handle: WindowHandle, id: WidgetId) {
|
||||
self.source.inner_invalidate_when_changed(handle, id);
|
||||
}
|
||||
|
||||
fn inner_sync_when_changed(&self, handle: WindowHandle) {
|
||||
self.source.inner_sync_when_changed(handle);
|
||||
}
|
||||
|
||||
fn inner_redraw_when_changed(&self, handle: WindowHandle) {
|
||||
self.source.inner_redraw_when_changed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_cycle_is_finite() {
|
||||
crate::initialize_tracing();
|
||||
|
|
|
|||
463
src/window.rs
463
src/window.rs
|
|
@ -19,7 +19,8 @@ use alot::LotId;
|
|||
use arboard::Clipboard;
|
||||
use figures::units::{Px, UPx};
|
||||
use figures::{
|
||||
Fraction, IntoSigned, IntoUnsigned, Point, Ranged, Rect, Round, ScreenScale, Size, UPx2D, Zero,
|
||||
FloatConversion, Fraction, IntoSigned, IntoUnsigned, Point, Ranged, Rect, Round, ScreenScale,
|
||||
Size, UPx2D, Zero,
|
||||
};
|
||||
use image::{DynamicImage, RgbImage, RgbaImage};
|
||||
use intentional::{Assert, Cast};
|
||||
|
|
@ -30,7 +31,7 @@ use kludgine::app::winit::event::{
|
|||
use kludgine::app::winit::keyboard::{
|
||||
Key, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey, SmolStr,
|
||||
};
|
||||
use kludgine::app::winit::window::{self, Cursor};
|
||||
use kludgine::app::winit::window::{self, Cursor, WindowLevel};
|
||||
use kludgine::app::{winit, WindowBehavior as _};
|
||||
use kludgine::cosmic_text::{fontdb, Family, FamilyOwned};
|
||||
use kludgine::drawing::Drawing;
|
||||
|
|
@ -38,6 +39,7 @@ use kludgine::shapes::Shape;
|
|||
use kludgine::wgpu::{self, CompositeAlphaMode, COPY_BYTES_PER_ROW_ALIGNMENT};
|
||||
use kludgine::{Color, DrawableExt, Kludgine, KludgineId, Origin, Texture};
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use sealed::Ize;
|
||||
use tracing::Level;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ use crate::animation::{
|
|||
AnimationTarget, Easing, LinearInterpolate, PercentBetween, Spawn, ZeroToOne,
|
||||
};
|
||||
use crate::app::{Application, Cushy, Open, PendingApp, Run};
|
||||
use crate::context::sealed::InvalidationStatus;
|
||||
use crate::context::sealed::{InvalidationStatus, Trackable as _};
|
||||
use crate::context::{
|
||||
AsEventContext, EventContext, Exclusive, GraphicsContext, LayoutContext, Trackable,
|
||||
WidgetContext,
|
||||
|
|
@ -56,8 +58,7 @@ use crate::styles::{Edges, FontFamilyList, ThemePair};
|
|||
use crate::tree::Tree;
|
||||
use crate::utils::ModifiersExt;
|
||||
use crate::value::{
|
||||
Destination, Dynamic, DynamicRead, DynamicReader, Generation, IntoDynamic, IntoValue, Source,
|
||||
Value,
|
||||
Destination, Dynamic, DynamicReader, IntoDynamic, IntoValue, Source, Tracked, Value,
|
||||
};
|
||||
use crate::widget::{
|
||||
Callback, EventHandling, MakeWidget, MountedWidget, OnceCallback, RootBehavior, WidgetId,
|
||||
|
|
@ -160,6 +161,14 @@ pub trait PlatformWindowImplementation {
|
|||
winit.set_max_inner_size::<PhysicalSize<u32>>(max_size.map(Into::into));
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures that this window will be redrawn when `value` has been updated.
|
||||
fn redraw_when_changed(&self, value: &impl Trackable, invalidation_status: &InvalidationStatus)
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
value.inner_redraw_when_changed(self.handle(invalidation_status.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformWindowImplementation for kludgine::app::Window<'_, WindowCommand> {
|
||||
|
|
@ -507,6 +516,17 @@ where
|
|||
occluded: Option<Dynamic<bool>>,
|
||||
focused: Option<Dynamic<bool>>,
|
||||
theme_mode: Option<Value<ThemeMode>>,
|
||||
content_protected: Option<Value<bool>>,
|
||||
cursor_hittest: Option<Value<bool>>,
|
||||
cursor_visible: Option<Value<bool>>,
|
||||
cursor_position: Option<Dynamic<Point<Px>>>,
|
||||
window_level: Option<Value<WindowLevel>>,
|
||||
decorated: Option<Value<bool>>,
|
||||
maximized: Option<Dynamic<bool>>,
|
||||
minimized: Option<Dynamic<bool>>,
|
||||
resizable: Option<Value<bool>>,
|
||||
resize_increments: Option<Value<Size<UPx>>>,
|
||||
visible: Option<Dynamic<bool>>,
|
||||
close_requested: Option<Callback<(), bool>>,
|
||||
}
|
||||
|
||||
|
|
@ -584,6 +604,17 @@ where
|
|||
close_requested: None,
|
||||
zoom: None,
|
||||
resize_to_fit: Value::Constant(false),
|
||||
content_protected: None,
|
||||
cursor_hittest: None,
|
||||
cursor_visible: None,
|
||||
cursor_position: None,
|
||||
window_level: None,
|
||||
decorated: None,
|
||||
maximized: None,
|
||||
minimized: None,
|
||||
resizable: None,
|
||||
resize_increments: None,
|
||||
visible: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -635,6 +666,84 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Prevents the window contents from being captured by other apps.
|
||||
pub fn content_protected(mut self, protected: impl IntoValue<bool>) -> Self {
|
||||
self.content_protected = Some(protected.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether the cursor should interact with this window or not.
|
||||
pub fn cursor_hittest(mut self, hittest: impl IntoValue<bool>) -> Self {
|
||||
self.cursor_hittest = Some(hittest.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the cursor is visible when above this window.
|
||||
pub fn cursor_visible(mut self, visible: impl IntoValue<bool>) -> Self {
|
||||
self.cursor_visible = Some(visible.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// A dynamic providing access to the window coordinate of the cursor, or
|
||||
/// -1, -1 if the cursor is not currently hovering the window.
|
||||
///
|
||||
/// In the future, this dynamic will also support setting the position of
|
||||
/// the cursor within the window.
|
||||
pub fn cursor_position(mut self, window_position: impl IntoDynamic<Point<Px>>) -> Self {
|
||||
self.cursor_position = Some(window_position.into_dynamic());
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether window decorations are shown around this window.
|
||||
pub fn decorated(mut self, decorated: impl IntoValue<bool>) -> Self {
|
||||
self.decorated = Some(decorated.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the level of this window.
|
||||
pub fn window_level(mut self, window_level: impl IntoValue<WindowLevel>) -> Self {
|
||||
self.window_level = Some(window_level.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Provides a dynamic that is updated with the minimized status of this
|
||||
/// window.
|
||||
pub fn minimized(mut self, minimized: impl IntoDynamic<bool>) -> Self {
|
||||
self.minimized = Some(minimized.into_dynamic());
|
||||
self
|
||||
}
|
||||
|
||||
/// Provides a dynamic that is updated with the maximized status of this
|
||||
/// window.
|
||||
pub fn maximized(mut self, maximized: impl IntoDynamic<bool>) -> Self {
|
||||
self.maximized = Some(maximized.into_dynamic());
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether the window is resizable by the user or not.
|
||||
pub fn resizable(mut self, resizable: impl IntoValue<bool>) -> Self {
|
||||
self.resizable = Some(resizable.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the increments in which the window can be resized.
|
||||
pub fn resize_increments(mut self, resize_increments: impl IntoValue<Size<UPx>>) -> Self {
|
||||
self.resize_increments = Some(resize_increments.into_value());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets this window to render with a transparent background.
|
||||
pub fn transparent(mut self) -> Self {
|
||||
self.attributes.transparent = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls the visibility of this window.
|
||||
pub fn visible(mut self, visible: impl IntoDynamic<bool>) -> Self {
|
||||
self.visible = Some(visible.into_dynamic());
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets this window's `zoom` factor.
|
||||
///
|
||||
/// The zoom factor is multiplied with the DPI scaling from the window
|
||||
|
|
@ -754,6 +863,17 @@ where
|
|||
close_requested: self.close_requested.map(|cb| Arc::new(Mutex::new(cb))),
|
||||
zoom: self.zoom.unwrap_or_else(|| Dynamic::new(Fraction::ONE)),
|
||||
resize_to_fit: self.resize_to_fit,
|
||||
content_protected: self.content_protected.unwrap_or_default(),
|
||||
cursor_hittest: self.cursor_hittest.unwrap_or_else(|| Value::Constant(true)),
|
||||
cursor_visible: self.cursor_visible.unwrap_or_else(|| Value::Constant(true)),
|
||||
cursor_position: self.cursor_position.unwrap_or_default(),
|
||||
window_level: self.window_level.unwrap_or_default(),
|
||||
decorated: self.decorated.unwrap_or_else(|| Value::Constant(true)),
|
||||
maximized: self.maximized.unwrap_or_default(),
|
||||
minimized: self.minimized.unwrap_or_default(),
|
||||
resizable: self.resizable.unwrap_or_else(|| Value::Constant(true)),
|
||||
resize_increments: self.resize_increments.unwrap_or_default(),
|
||||
visible: self.visible.unwrap_or_default(),
|
||||
}),
|
||||
pending: self.pending,
|
||||
},
|
||||
|
|
@ -820,8 +940,7 @@ struct OpenWindow<T> {
|
|||
initial_frame: bool,
|
||||
occluded: Dynamic<bool>,
|
||||
focused: Dynamic<bool>,
|
||||
inner_size: Dynamic<Size<UPx>>,
|
||||
inner_size_generation: Generation,
|
||||
inner_size: Tracked<Dynamic<Size<UPx>>>,
|
||||
keyboard_activated: Option<WidgetId>,
|
||||
min_inner_size: Option<Size<UPx>>,
|
||||
max_inner_size: Option<Size<UPx>>,
|
||||
|
|
@ -835,9 +954,19 @@ struct OpenWindow<T> {
|
|||
on_closed: Option<OnceCallback>,
|
||||
vsync: bool,
|
||||
dpi_scale: Dynamic<Fraction>,
|
||||
zoom: Dynamic<Fraction>,
|
||||
zoom_generation: Generation,
|
||||
zoom: Tracked<Dynamic<Fraction>>,
|
||||
close_requested: Option<Arc<Mutex<Callback<(), bool>>>>,
|
||||
content_protected: Tracked<Value<bool>>,
|
||||
cursor_hittest: Tracked<Value<bool>>,
|
||||
cursor_visible: Tracked<Value<bool>>,
|
||||
cursor_position: Tracked<Dynamic<Point<Px>>>,
|
||||
window_level: Tracked<Value<WindowLevel>>,
|
||||
decorated: Tracked<Value<bool>>,
|
||||
maximized: Tracked<Dynamic<bool>>,
|
||||
minimized: Tracked<Dynamic<bool>>,
|
||||
resizable: Tracked<Value<bool>>,
|
||||
resize_increments: Tracked<Value<Size<UPx>>>,
|
||||
visible: Tracked<Dynamic<bool>>,
|
||||
}
|
||||
|
||||
impl<T> OpenWindow<T>
|
||||
|
|
@ -1200,19 +1329,12 @@ where
|
|||
cushy.fonts.clone(),
|
||||
graphics.font_system().db_mut(),
|
||||
);
|
||||
let occluded = settings.occluded;
|
||||
let focused = settings.focused;
|
||||
let theme = settings.theme.unwrap_or_default();
|
||||
let inner_size = settings.inner_size;
|
||||
let on_closed = settings.on_closed;
|
||||
let close_requested = settings.close_requested;
|
||||
let vsync = settings.vsync;
|
||||
let zoom = settings.zoom;
|
||||
let resize_to_fit = settings.resize_to_fit;
|
||||
let dpi_scale = Dynamic::new(graphics.dpi_scale());
|
||||
|
||||
inner_size.set(window.inner_size());
|
||||
|
||||
let dpi_scale = Dynamic::new(graphics.dpi_scale());
|
||||
|
||||
let theme_mode = match settings.theme_mode.take() {
|
||||
Some(Value::Dynamic(dynamic)) => {
|
||||
dynamic.set(window.theme().into());
|
||||
|
|
@ -1221,11 +1343,11 @@ where
|
|||
Some(Value::Constant(mode)) => Value::Constant(mode),
|
||||
None => Value::dynamic(window.theme().into()),
|
||||
};
|
||||
let transparent = settings.transparent;
|
||||
|
||||
let tree = Tree::default();
|
||||
let root = tree.push_boxed(behavior.make_root(), None);
|
||||
|
||||
let theme = settings.theme.unwrap_or_default();
|
||||
let (current_theme, theme) = match theme {
|
||||
Value::Constant(theme) => (theme, None),
|
||||
Value::Dynamic(dynamic) => (dynamic.get(), Some(dynamic.into_reader())),
|
||||
|
|
@ -1244,26 +1366,35 @@ where
|
|||
mouse_buttons: AHashMap::default(),
|
||||
redraw_status,
|
||||
initial_frame: true,
|
||||
occluded,
|
||||
focused,
|
||||
inner_size_generation: inner_size.generation(),
|
||||
inner_size,
|
||||
occluded: settings.occluded,
|
||||
focused: settings.focused,
|
||||
inner_size: Tracked::from(inner_size),
|
||||
keyboard_activated: None,
|
||||
min_inner_size: None,
|
||||
max_inner_size: None,
|
||||
resize_to_fit,
|
||||
resize_to_fit: settings.resize_to_fit,
|
||||
current_theme,
|
||||
theme,
|
||||
theme_mode,
|
||||
transparent,
|
||||
transparent: settings.transparent,
|
||||
fonts,
|
||||
cushy,
|
||||
on_closed,
|
||||
vsync,
|
||||
close_requested,
|
||||
on_closed: settings.on_closed,
|
||||
vsync: settings.vsync,
|
||||
close_requested: settings.close_requested,
|
||||
dpi_scale,
|
||||
zoom_generation: zoom.generation(),
|
||||
zoom,
|
||||
zoom: Tracked::from(settings.zoom),
|
||||
content_protected: Tracked::from(settings.content_protected),
|
||||
cursor_hittest: Tracked::from(settings.cursor_hittest),
|
||||
cursor_visible: Tracked::from(settings.cursor_visible),
|
||||
cursor_position: Tracked::from(settings.cursor_position),
|
||||
window_level: Tracked::from(settings.window_level),
|
||||
decorated: Tracked::from(settings.decorated),
|
||||
maximized: Tracked::from(settings.maximized),
|
||||
minimized: Tracked::from(settings.minimized),
|
||||
resizable: Tracked::from(settings.resizable),
|
||||
resize_increments: Tracked::from(settings.resize_increments),
|
||||
visible: Tracked::from(settings.visible),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1277,25 +1408,23 @@ where
|
|||
|
||||
self.redraw_status.refresh_received();
|
||||
graphics.reset_text_attributes();
|
||||
let zoom = self.zoom.read();
|
||||
if zoom.generation() != self.zoom_generation {
|
||||
if let Some(zoom) = self.zoom.updated() {
|
||||
graphics.set_zoom(*zoom);
|
||||
self.redraw_status.invalidate(self.root.id());
|
||||
}
|
||||
|
||||
self.tree
|
||||
.new_frame(self.redraw_status.invalidations().drain());
|
||||
|
||||
drop(zoom);
|
||||
}
|
||||
|
||||
fn prepare<W>(&mut self, window: W, graphics: &mut kludgine::Graphics<'_>)
|
||||
fn prepare<W>(&mut self, mut window: W, graphics: &mut kludgine::Graphics<'_>)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
let cushy = self.cushy.clone();
|
||||
let _guard = cushy.enter_runtime();
|
||||
|
||||
self.synchronize_platform_window(&mut window);
|
||||
self.new_frame(graphics);
|
||||
|
||||
let resize_to_fit = self.resize_to_fit.get();
|
||||
|
|
@ -1307,7 +1436,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics);
|
||||
|
|
@ -1357,10 +1486,8 @@ where
|
|||
let render_size = actual_size.min(window_size);
|
||||
layout_context.redraw_when_changed(&self.inner_size);
|
||||
layout_context.invalidate_when_changed(&self.resize_to_fit);
|
||||
let inner_size_generation = self.inner_size.generation();
|
||||
if self.inner_size_generation != inner_size_generation {
|
||||
layout_context.request_inner_size(self.inner_size.get());
|
||||
self.inner_size_generation = inner_size_generation;
|
||||
if let Some(new_size) = self.inner_size.updated() {
|
||||
layout_context.request_inner_size(*new_size);
|
||||
} else if actual_size != window_size && !resizable {
|
||||
let mut new_size = actual_size;
|
||||
if let Some(min_size) = self.min_inner_size {
|
||||
|
|
@ -1407,7 +1534,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
)) {
|
||||
self.should_close = true;
|
||||
|
|
@ -1417,19 +1544,101 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn resized(&mut self, new_size: Size<UPx>) {
|
||||
fn resized<W>(&mut self, new_size: Size<UPx>, window: &W)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
self.inner_size.set(new_size);
|
||||
// We want to prevent a resize request for this resized event.
|
||||
self.inner_size_generation = self.inner_size.generation();
|
||||
self.inner_size.mark_read();
|
||||
self.update_ized(window);
|
||||
self.root.invalidate();
|
||||
}
|
||||
|
||||
fn update_ized<W>(&mut self, window: &W)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
if let Some(winit) = window.winit() {
|
||||
// TODO should these be supported outside of winit? Put in a feature
|
||||
// request if you read this and need them.
|
||||
self.maximized.set_and_read(winit.is_maximized());
|
||||
if let Some(minimized) = winit.is_minimized() {
|
||||
self.minimized.set_and_read(minimized);
|
||||
}
|
||||
self.decorated.set_and_read(winit.is_decorated());
|
||||
}
|
||||
}
|
||||
|
||||
fn synchronize_platform_window<W>(&mut self, window: &mut W)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
self.update_ized(window);
|
||||
if let Some(winit) = window.winit() {
|
||||
self.content_protected
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(protected) = self.content_protected.updated() {
|
||||
winit.set_content_protected(*protected);
|
||||
}
|
||||
self.cursor_hittest
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(hit) = self.cursor_hittest.updated() {
|
||||
let _ = winit.set_cursor_hittest(*hit);
|
||||
}
|
||||
self.cursor_visible
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(visible) = self.cursor_visible.updated() {
|
||||
winit.set_cursor_visible(*visible);
|
||||
}
|
||||
self.window_level
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(window_level) = self.window_level.updated() {
|
||||
winit.set_window_level(*window_level);
|
||||
}
|
||||
self.decorated
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(decorated) = self.decorated.updated() {
|
||||
winit.set_decorations(*decorated);
|
||||
}
|
||||
self.resize_increments
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(resize_increments) = self.resize_increments.updated() {
|
||||
let increments: Option<PhysicalSize<f32>> =
|
||||
if resize_increments.width > 0 || resize_increments.height > 0 {
|
||||
Some(PhysicalSize::new(
|
||||
resize_increments.width.into_float(),
|
||||
resize_increments.height.into_float(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
winit.set_resize_increments(increments);
|
||||
}
|
||||
self.visible
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(visible) = self.visible.updated() {
|
||||
winit.set_visible(*visible);
|
||||
}
|
||||
self.resizable
|
||||
.inner_sync_when_changed(window.handle(self.redraw_status.clone()));
|
||||
if let Some(resizable) = self.resizable.updated() {
|
||||
winit.set_resizable(*resizable);
|
||||
window.set_needs_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_focused(&mut self, focused: bool) {
|
||||
self.focused.set(focused);
|
||||
}
|
||||
|
||||
pub fn set_occluded(&mut self, occluded: bool) {
|
||||
pub fn set_occluded<W>(&mut self, window: &W, occluded: bool)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
self.occluded.set(occluded);
|
||||
self.update_ized(window);
|
||||
}
|
||||
|
||||
pub fn keyboard_input<W>(
|
||||
|
|
@ -1452,7 +1661,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
let target = self.tree.focused_widget().unwrap_or(self.root.node_id);
|
||||
|
|
@ -1503,7 +1712,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
let widget = self
|
||||
|
|
@ -1547,7 +1756,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
let widget = self
|
||||
|
|
@ -1592,12 +1801,13 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
|
||||
let location = position.into();
|
||||
self.cursor.location = Some(location);
|
||||
self.cursor_position.set_and_read(location);
|
||||
|
||||
EventContext::new(
|
||||
WidgetContext::new(
|
||||
|
|
@ -1643,6 +1853,9 @@ where
|
|||
{
|
||||
let cushy = self.cushy.clone();
|
||||
let _guard = cushy.enter_runtime();
|
||||
self.cursor.location = None;
|
||||
self.cursor_position
|
||||
.set_and_read(Point::squared(Px::new(-1)));
|
||||
if self.cursor.widget.take().is_some() {
|
||||
let mut window = RunningWindow::new(
|
||||
window,
|
||||
|
|
@ -1651,7 +1864,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
|
||||
|
|
@ -1690,7 +1903,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
match state {
|
||||
|
|
@ -1863,7 +2076,7 @@ where
|
|||
window: kludgine::app::Window<'_, WindowCommand>,
|
||||
_kludgine: &mut Kludgine,
|
||||
) {
|
||||
self.set_occluded(window.ocluded());
|
||||
self.set_occluded(&window, window.ocluded());
|
||||
}
|
||||
|
||||
fn render<'pass>(
|
||||
|
|
@ -1909,7 +2122,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
),
|
||||
)
|
||||
|
|
@ -1957,7 +2170,7 @@ where
|
|||
window: kludgine::app::Window<'_, WindowCommand>,
|
||||
_kludgine: &mut Kludgine,
|
||||
) {
|
||||
self.resized(window.inner_size());
|
||||
self.resized(window.inner_size(), &window);
|
||||
}
|
||||
|
||||
// fn theme_changed(&mut self, window: kludgine::app::Window<'_, ()>) {}
|
||||
|
|
@ -2059,6 +2272,10 @@ where
|
|||
WindowCommand::Redraw => {
|
||||
window.set_needs_redraw();
|
||||
}
|
||||
WindowCommand::Sync => {
|
||||
self.synchronize_platform_window(&mut window);
|
||||
self.redraw_status.sync_received();
|
||||
}
|
||||
WindowCommand::RequestClose => {
|
||||
let mut window = RunningWindow::new(
|
||||
window,
|
||||
|
|
@ -2067,7 +2284,7 @@ where
|
|||
&self.cushy,
|
||||
&self.focused,
|
||||
&self.occluded,
|
||||
&self.inner_size,
|
||||
self.inner_size.source(),
|
||||
&self.close_requested,
|
||||
);
|
||||
if self.behavior.close_requested(&mut window) {
|
||||
|
|
@ -2077,8 +2294,72 @@ where
|
|||
WindowCommand::SetTitle(new_title) => {
|
||||
window.set_title(&new_title);
|
||||
}
|
||||
WindowCommand::ResetDeadKeys => {
|
||||
window.winit().reset_dead_keys();
|
||||
}
|
||||
WindowCommand::RequestUserAttention(request_type) => {
|
||||
window.winit().request_user_attention(request_type);
|
||||
}
|
||||
WindowCommand::Focus => {
|
||||
window.winit().focus_window();
|
||||
}
|
||||
WindowCommand::Ize(ize) => {
|
||||
let (minimize, maximize) = match ize {
|
||||
Some(Ize::Maximize) => (false, true),
|
||||
Some(Ize::Minimize) => (true, false),
|
||||
None => (false, false),
|
||||
};
|
||||
if window
|
||||
.winit()
|
||||
.is_minimized()
|
||||
.map_or(true, |minimized| minimized != minimize)
|
||||
{
|
||||
window.winit().set_minimized(minimize);
|
||||
}
|
||||
if window.winit().is_maximized() != maximize {
|
||||
window.winit().set_maximized(maximize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fn dropped_file(
|
||||
// &mut self,
|
||||
// window: kludgine::app::Window<'_, WindowCommand>,
|
||||
// kludgine: &mut Kludgine,
|
||||
// path: std::path::PathBuf,
|
||||
// ) {
|
||||
// }
|
||||
|
||||
// fn hovered_file(
|
||||
// &mut self,
|
||||
// window: kludgine::app::Window<'_, WindowCommand>,
|
||||
// kludgine: &mut Kludgine,
|
||||
// path: std::path::PathBuf,
|
||||
// ) {
|
||||
// }
|
||||
|
||||
// fn hovered_file_cancelled(
|
||||
// &mut self,
|
||||
// window: kludgine::app::Window<'_, WindowCommand>,
|
||||
// kludgine: &mut Kludgine,
|
||||
// ) {
|
||||
// }
|
||||
|
||||
// fn received_character(
|
||||
// &mut self,
|
||||
// window: kludgine::app::Window<'_, WindowCommand>,
|
||||
// kludgine: &mut Kludgine,
|
||||
// char: char,
|
||||
// ) {
|
||||
// }
|
||||
|
||||
// fn modifiers_changed(
|
||||
// &mut self,
|
||||
// window: kludgine::app::Window<'_, WindowCommand>,
|
||||
// kludgine: &mut Kludgine,
|
||||
// ) {
|
||||
// }
|
||||
}
|
||||
|
||||
impl<Behavior> Drop for OpenWindow<Behavior> {
|
||||
|
|
@ -2112,9 +2393,10 @@ pub(crate) mod sealed {
|
|||
use std::num::NonZeroU32;
|
||||
use std::sync::Arc;
|
||||
|
||||
use figures::units::UPx;
|
||||
use figures::units::{Px, UPx};
|
||||
use figures::{Fraction, Point, Size};
|
||||
use image::DynamicImage;
|
||||
use kludgine::app::winit::window::{UserAttentionType, WindowLevel};
|
||||
use kludgine::Color;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
|
|
@ -2156,15 +2438,37 @@ pub(crate) mod sealed {
|
|||
pub multisample_count: NonZeroU32,
|
||||
pub resize_to_fit: Value<bool>,
|
||||
pub close_requested: Option<Arc<Mutex<Callback<(), bool>>>>,
|
||||
pub content_protected: Value<bool>,
|
||||
pub cursor_hittest: Value<bool>,
|
||||
pub cursor_visible: Value<bool>,
|
||||
pub cursor_position: Dynamic<Point<Px>>,
|
||||
pub window_level: Value<WindowLevel>,
|
||||
pub decorated: Value<bool>,
|
||||
pub maximized: Dynamic<bool>,
|
||||
pub minimized: Dynamic<bool>,
|
||||
pub resizable: Value<bool>,
|
||||
pub resize_increments: Value<Size<UPx>>,
|
||||
pub visible: Dynamic<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WindowCommand {
|
||||
Redraw,
|
||||
Sync,
|
||||
RequestClose,
|
||||
ResetDeadKeys,
|
||||
RequestUserAttention(Option<UserAttentionType>),
|
||||
Focus,
|
||||
Ize(Option<Ize>),
|
||||
SetTitle(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Ize {
|
||||
Maximize,
|
||||
Minimize,
|
||||
}
|
||||
|
||||
pub trait CaptureFormat {
|
||||
const HAS_ALPHA: bool;
|
||||
|
||||
|
|
@ -2312,6 +2616,12 @@ impl WindowHandle {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sync(&self) {
|
||||
if self.redraw_status.should_send_sync() {
|
||||
self.inner.send(WindowCommand::Sync);
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks `widget` as invalidated, and if needed, refreshes the window.
|
||||
pub fn invalidate(&self, widget: WidgetId) {
|
||||
if self.redraw_status.invalidate(widget) {
|
||||
|
|
@ -2358,6 +2668,11 @@ impl InnerWindowHandle {
|
|||
WindowCommand::Redraw => state.redraw_target.set(RedrawTarget::Now),
|
||||
WindowCommand::RequestClose => state.close_requested.set(true),
|
||||
WindowCommand::SetTitle(title) => state.title.set(title),
|
||||
WindowCommand::ResetDeadKeys
|
||||
| WindowCommand::RequestUserAttention(_)
|
||||
| WindowCommand::Focus
|
||||
| WindowCommand::Ize(_)
|
||||
| WindowCommand::Sync => {}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -2730,6 +3045,17 @@ impl StandaloneWindowBuilder {
|
|||
close_requested: None,
|
||||
zoom: self.zoom,
|
||||
resize_to_fit: self.resize_to_fit,
|
||||
content_protected: Value::Constant(false),
|
||||
cursor_hittest: Value::Constant(true),
|
||||
cursor_visible: Value::Constant(true),
|
||||
cursor_position: Dynamic::default(),
|
||||
window_level: Value::default(),
|
||||
decorated: Value::Constant(true),
|
||||
maximized: Dynamic::new(false),
|
||||
minimized: Dynamic::new(false),
|
||||
resizable: Value::Constant(true),
|
||||
resize_increments: Value::default(),
|
||||
visible: Dynamic::new(true),
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -2846,8 +3172,11 @@ impl CushyWindow {
|
|||
/// This should only be set to true if the window is not visible at all to
|
||||
/// the end user due to being offscreen, minimized, or fully hidden behind
|
||||
/// other windows.
|
||||
pub fn set_occluded(&mut self, occluded: bool) {
|
||||
self.window.set_occluded(occluded);
|
||||
pub fn set_occluded<W>(&mut self, window: &W, occluded: bool)
|
||||
where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
self.window.set_occluded(window, occluded);
|
||||
}
|
||||
|
||||
/// Requests that the window close.
|
||||
|
|
@ -2876,15 +3205,18 @@ impl CushyWindow {
|
|||
}
|
||||
|
||||
/// Updates the dimensions and DPI scaling of the window.
|
||||
pub fn resize(
|
||||
pub fn resize<W>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
new_size: Size<UPx>,
|
||||
new_scale: impl Into<Fraction>,
|
||||
new_zoom: impl Into<Fraction>,
|
||||
queue: &wgpu::Queue,
|
||||
) {
|
||||
) where
|
||||
W: PlatformWindowImplementation,
|
||||
{
|
||||
self.kludgine.resize(new_size, new_scale, new_zoom, queue);
|
||||
self.window.resized(new_size);
|
||||
self.window.resized(new_size, window);
|
||||
}
|
||||
|
||||
/// Provide keyboard input to this virtual window.
|
||||
|
|
@ -3070,7 +3402,7 @@ impl VirtualWindow {
|
|||
/// the end user due to being offscreen, minimized, or fully hidden behind
|
||||
/// other windows.
|
||||
pub fn set_occluded(&mut self, occluded: bool) {
|
||||
self.cushy.set_occluded(occluded);
|
||||
self.cushy.set_occluded(&&mut self.state, occluded);
|
||||
}
|
||||
|
||||
/// Returns true if this window should no longer be open.
|
||||
|
|
@ -3102,8 +3434,13 @@ impl VirtualWindow {
|
|||
new_scale: impl Into<Fraction>,
|
||||
queue: &wgpu::Queue,
|
||||
) {
|
||||
self.cushy
|
||||
.resize(new_size, new_scale, self.cushy.kludgine.zoom(), queue);
|
||||
self.cushy.resize(
|
||||
&&mut self.state,
|
||||
new_size,
|
||||
new_scale,
|
||||
self.cushy.kludgine.zoom(),
|
||||
queue,
|
||||
);
|
||||
}
|
||||
|
||||
/// Provide keyboard input to this virtual window.
|
||||
|
|
|
|||
Loading…
Reference in a new issue