fix: smooth fullscreen and direct geometry transitions
This commit is contained in:
1 parent
b4fee6f72b
commit
83e36b0d2d
2 files changed
+67
-12
No files matched your search
@@ -118,6 +118,7 @@ pub struct Ferese {
|
|||||||
output_identity_ids: HashMap<String, OutputId>,
|
output_identity_ids: HashMap<String, OutputId>,
|
||||||
pub window_ids: HashMap<Window, WindowId>,
|
pub window_ids: HashMap<Window, WindowId>,
|
||||||
pub window_geometry: HashMap<WindowId, WindowGeometry>,
|
pub window_geometry: HashMap<WindowId, WindowGeometry>,
|
||||||
|
pub(crate) window_decoration_progress: HashMap<WindowId, AnimatedValue>,
|
||||||
pub(crate) window_borders: HashMap<WindowId, crate::winit::WindowBorderBuffers>,
|
pub(crate) window_borders: HashMap<WindowId, crate::winit::WindowBorderBuffers>,
|
||||||
pub(crate) window_shadows: HashMap<WindowId, crate::winit::WindowShadowBuffers>,
|
pub(crate) window_shadows: HashMap<WindowId, crate::winit::WindowShadowBuffers>,
|
||||||
pub(crate) rounded_clip_programs: HashMap<ErasedContextId, crate::winit::RoundedClipPrograms>,
|
pub(crate) rounded_clip_programs: HashMap<ErasedContextId, crate::winit::RoundedClipPrograms>,
|
||||||
@@ -428,6 +429,7 @@ impl Ferese {
|
|||||||
output_identity_ids: HashMap::new(),
|
output_identity_ids: HashMap::new(),
|
||||||
window_ids: HashMap::new(),
|
window_ids: HashMap::new(),
|
||||||
window_geometry: HashMap::new(),
|
window_geometry: HashMap::new(),
|
||||||
|
window_decoration_progress: HashMap::new(),
|
||||||
window_borders: HashMap::new(),
|
window_borders: HashMap::new(),
|
||||||
window_shadows: HashMap::new(),
|
window_shadows: HashMap::new(),
|
||||||
rounded_clip_programs: HashMap::new(),
|
rounded_clip_programs: HashMap::new(),
|
||||||
@@ -819,6 +821,7 @@ impl Ferese {
|
|||||||
|
|
||||||
self.space.unmap_elem(window);
|
self.space.unmap_elem(window);
|
||||||
self.window_geometry.remove(&id);
|
self.window_geometry.remove(&id);
|
||||||
|
self.window_decoration_progress.remove(&id);
|
||||||
self.window_borders.remove(&id);
|
self.window_borders.remove(&id);
|
||||||
self.window_shadows.remove(&id);
|
self.window_shadows.remove(&id);
|
||||||
self.closing_windows.remove(&id);
|
self.closing_windows.remove(&id);
|
||||||
@@ -970,8 +973,34 @@ impl Ferese {
|
|||||||
let now = self.start_time.elapsed();
|
let now = self.start_time.elapsed();
|
||||||
|
|
||||||
let mut scrolling_world_x = HashMap::new();
|
let mut scrolling_world_x = HashMap::new();
|
||||||
|
// map_element also raises windows. Keep relayout's HashMap iteration
|
||||||
|
// order from randomly changing the stack during overlapping animations.
|
||||||
|
let stacking_order = self
|
||||||
|
.space
|
||||||
|
.elements()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(index, window)| self.window_ids.get(window).map(|id| (*id, index)))
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
placements.sort_by_key(|(_, id, ..)| stacking_order.get(id).copied().unwrap_or(usize::MAX));
|
||||||
|
let mut fullscreen_transitions = Vec::new();
|
||||||
|
|
||||||
for (window, id, rect, is_fullscreen, is_floating, scrolling, couple_width) in placements {
|
for (window, id, rect, is_fullscreen, is_floating, scrolling, couple_width) in placements {
|
||||||
|
let decoration_target = if is_fullscreen { 0.0 } else { 1.0 };
|
||||||
|
let decorations = self
|
||||||
|
.window_decoration_progress
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| AnimatedValue::new(decoration_target));
|
||||||
|
if decorations.target != decoration_target {
|
||||||
|
fullscreen_transitions.push(window.clone());
|
||||||
|
}
|
||||||
|
decorations.set_target(decoration_target);
|
||||||
|
if !self.animations_enabled {
|
||||||
|
decorations.snap();
|
||||||
|
}
|
||||||
|
// A viewport-coupled width must never override fullscreen/floating geometry.
|
||||||
|
if scrolling.is_none() {
|
||||||
|
self.viewport_coupled_widths.remove(&id);
|
||||||
|
}
|
||||||
let committed_size = client_size(&window);
|
let committed_size = client_size(&window);
|
||||||
let had_geometry = self.window_geometry.contains_key(&id);
|
let had_geometry = self.window_geometry.contains_key(&id);
|
||||||
let geometry = self
|
let geometry = self
|
||||||
@@ -1070,6 +1099,11 @@ impl Ferese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.scrolling_world_x = scrolling_world_x;
|
self.scrolling_world_x = scrolling_world_x;
|
||||||
|
// Neighbours are remapped on fullscreen exit; the shrinking window must
|
||||||
|
// remain above them. Subsequent animation ticks preserve this ordering.
|
||||||
|
for window in fullscreen_transitions {
|
||||||
|
self.space.raise_element(&window, false);
|
||||||
|
}
|
||||||
|
|
||||||
crate::backends::direct::render_all(self);
|
crate::backends::direct::render_all(self);
|
||||||
}
|
}
|
||||||
@@ -1141,6 +1175,13 @@ impl Ferese {
|
|||||||
|
|
||||||
let mut settled_coupled_widths = Vec::new();
|
let mut settled_coupled_widths = Vec::new();
|
||||||
for (window, id) in windows {
|
for (window, id) in windows {
|
||||||
|
if let Some(decorations) = self.window_decoration_progress.get_mut(&id) {
|
||||||
|
if self.animations_enabled {
|
||||||
|
active_animation |= decorations.advance(delta, self.spring_config);
|
||||||
|
} else {
|
||||||
|
decorations.snap();
|
||||||
|
}
|
||||||
|
}
|
||||||
let Some(geometry) = self.window_geometry.get_mut(&id) else {
|
let Some(geometry) = self.window_geometry.get_mut(&id) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -1535,7 +1576,21 @@ impl Ferese {
|
|||||||
|
|
||||||
if let Err(error) = self.workspaces.set_floating_rect(id, rect) {
|
if let Err(error) = self.workspaces.set_floating_rect(id, rect) {
|
||||||
tracing::error!(%error, ?id, "failed to update floating window geometry");
|
tracing::error!(%error, ?id, "failed to update floating window geometry");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Direct manipulation follows the pointer, including while the client is
|
||||||
|
// still drawing its next buffer. Rendering and hit testing share this rect.
|
||||||
|
let geometry = self
|
||||||
|
.window_geometry
|
||||||
|
.entry(id)
|
||||||
|
.or_insert_with(|| WindowGeometry::new(rect, client_size(window)));
|
||||||
|
geometry.set_logical_target(rect, self.start_time.elapsed());
|
||||||
|
geometry.visual.snap();
|
||||||
|
self.scrolling_world_x.remove(&id);
|
||||||
|
self.viewport_coupled_widths.remove(&id);
|
||||||
|
self.space.map_element(window.clone(), location, false);
|
||||||
|
crate::backends::direct::render_all(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch_workspace(&mut self, index: u32) {
|
pub fn switch_workspace(&mut self, index: u32) {
|
||||||
|
|||||||
+12
-12
@@ -574,21 +574,20 @@ fn output_elements(
|
|||||||
let visual = state.window_geometry.get(&id)?.visual.current;
|
let visual = state.window_geometry.get(&id)?.visual.current;
|
||||||
let (close_scale, close_alpha) = state.closing_visual(id);
|
let (close_scale, close_alpha) = state.closing_visual(id);
|
||||||
let visual = scaled_visual_rect(visual, close_scale);
|
let visual = scaled_visual_rect(visual, close_scale);
|
||||||
let fullscreen = state
|
let decoration_progress = state
|
||||||
.workspaces
|
.window_decoration_progress
|
||||||
.workspace_for_window(id)
|
.get(&id)
|
||||||
.and_then(|workspace| state.workspaces.workspace(workspace))
|
.map_or(1.0, |progress| progress.current.clamp(0.0, 1.0));
|
||||||
.is_some_and(|workspace| workspace.fullscreen == Some(id));
|
|
||||||
|
|
||||||
Some((window.clone(), id, visual, fullscreen, close_alpha))
|
Some((window.clone(), id, visual, decoration_progress, close_alpha))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for (window, id, visual, fullscreen, close_alpha) in windows {
|
for (window, id, visual, decoration_progress, close_alpha) in windows {
|
||||||
let constrain = rounded_visual_rect(visual, output_geometry.loc);
|
let constrain = rounded_visual_rect(visual, output_geometry.loc);
|
||||||
|
|
||||||
if !fullscreen && let Some(programs) = rounded_clip_program.clone() {
|
if let Some(programs) = rounded_clip_program.clone() {
|
||||||
let window_radius = state.theme_settings.window_radius;
|
let window_radius = state.theme_settings.window_radius * decoration_progress;
|
||||||
let shadow_offset_y = state.theme_settings.shadow_offset_y;
|
let shadow_offset_y = state.theme_settings.shadow_offset_y;
|
||||||
let shadow_blur = state.theme_settings.shadow_blur;
|
let shadow_blur = state.theme_settings.shadow_blur;
|
||||||
let shadow_opacity = state.theme_settings.shadow_opacity;
|
let shadow_opacity = state.theme_settings.shadow_opacity;
|
||||||
@@ -604,7 +603,8 @@ fn output_elements(
|
|||||||
} else {
|
} else {
|
||||||
state.theme_settings.border_color.0
|
state.theme_settings.border_color.0
|
||||||
};
|
};
|
||||||
let border_color = color_with_alpha(border_color, close_alpha);
|
let border_color =
|
||||||
|
color_with_alpha(border_color, close_alpha * decoration_progress as f32);
|
||||||
|
|
||||||
if let Some(border) = window_border_element(
|
if let Some(border) = window_border_element(
|
||||||
state,
|
state,
|
||||||
@@ -629,7 +629,7 @@ fn output_elements(
|
|||||||
window_radius,
|
window_radius,
|
||||||
shadow_offset_y,
|
shadow_offset_y,
|
||||||
shadow_blur,
|
shadow_blur,
|
||||||
shadow_opacity * f64::from(close_alpha),
|
shadow_opacity * f64::from(close_alpha) * decoration_progress,
|
||||||
shadow_color,
|
shadow_color,
|
||||||
output,
|
output,
|
||||||
&programs,
|
&programs,
|
||||||
@@ -656,7 +656,7 @@ fn output_elements(
|
|||||||
renderer,
|
renderer,
|
||||||
&window,
|
&window,
|
||||||
constrain.loc,
|
constrain.loc,
|
||||||
1.0,
|
close_alpha,
|
||||||
scale,
|
scale,
|
||||||
constrain,
|
constrain,
|
||||||
ConstrainBehavior {
|
ConstrainBehavior {
|
||||||
|
|||||||
Reference in new issue
Block a user