tiling: Add code to render group hints

This commit is contained in:
Victoria Brekenfeld 2023-05-17 19:46:21 +02:00
parent 84b3213146
commit 4ea0136a9b
8 changed files with 739 additions and 276 deletions

View file

@ -1,2 +1,2 @@
[toolchain]
channel = "1.65"
channel = "1.66"

View file

@ -3,6 +3,8 @@
use std::{
borrow::{Borrow, BorrowMut},
cell::RefCell,
collections::HashMap,
sync::Weak,
};
#[cfg(feature = "debug")]
@ -12,8 +14,8 @@ use crate::{
};
use crate::{
shell::{
element::window::CosmicWindowRenderElement, layout::floating::SeatMoveGrabState,
CosmicMappedRenderElement,
element::window::CosmicWindowRenderElement, focus::target::WindowGroup,
layout::floating::SeatMoveGrabState, CosmicMapped, CosmicMappedRenderElement,
},
state::{Common, Fps},
utils::prelude::SeatExt,
@ -47,7 +49,7 @@ use smithay::{
},
},
output::Output,
utils::{Logical, Physical, Point, Rectangle, Size},
utils::{IsAlive, Logical, Physical, Point, Rectangle, Size},
wayland::{
dmabuf::get_dmabuf,
shm::{shm_format_to_fourcc, with_buffer_contents},
@ -66,11 +68,54 @@ pub type GlMultiFrame<'a, 'b, 'frame> =
MultiFrame<'a, 'a, 'b, 'frame, GbmGlesBackend<GlowRenderer>, GbmGlesBackend<GlowRenderer>>;
pub static CLEAR_COLOR: [f32; 4] = [0.153, 0.161, 0.165, 1.0];
pub static ACTIVE_GROUP_COLOR: [f32; 3] = [0.678, 0.635, 0.619];
pub static GROUP_COLOR: [f32; 3] = [0.431, 0.404, 0.396];
pub static FOCUS_INDICATOR_COLOR: [f32; 3] = [0.580, 0.921, 0.921];
pub static FOCUS_INDICATOR_SHADER: &str = include_str!("./shaders/focus_indicator.frag");
pub struct IndicatorShader(pub GlesPixelProgram);
struct IndicatorElement(pub RefCell<PixelShaderElement>);
#[derive(Clone)]
pub enum Key {
Group(Weak<()>),
Window(CosmicMapped),
}
impl std::hash::Hash for Key {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Key::Group(arc) => (arc.as_ptr() as usize).hash(state),
Key::Window(window) => window.hash(state),
}
}
}
impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Key::Group(g1), Key::Group(g2)) => Weak::ptr_eq(g1, g2),
(Key::Window(w1), Key::Window(w2)) => w1 == w2,
_ => false,
}
}
}
impl Eq for Key {}
impl From<CosmicMapped> for Key {
fn from(window: CosmicMapped) -> Self {
Key::Window(window)
}
}
impl From<WindowGroup> for Key {
fn from(group: WindowGroup) -> Self {
Key::Group(group.alive.clone())
}
}
#[derive(PartialEq)]
struct IndicatorSettings {
thickness: u8,
alpha: f32,
color: [f32; 3],
}
type IndicatorCache = RefCell<HashMap<Key, (IndicatorSettings, PixelShaderElement)>>;
impl IndicatorShader {
pub fn get<R: AsGlowRenderer>(renderer: &R) -> GlesPixelProgram {
@ -85,50 +130,63 @@ impl IndicatorShader {
pub fn element<R: AsGlowRenderer>(
renderer: &R,
key: impl Into<Key>,
geo: Rectangle<i32, Logical>,
thickness: u8,
alpha: f32,
color: [f32; 3],
) -> PixelShaderElement {
let thickness: f32 = thickness as f32;
let thickness_loc = (thickness as i32, thickness as i32);
let thickness_size = ((thickness * 2.0) as i32, (thickness * 2.0) as i32);
let geo = Rectangle::from_loc_and_size(
geo.loc - Point::from(thickness_loc),
geo.size + Size::from(thickness_size),
);
let settings = IndicatorSettings {
thickness,
alpha,
color,
};
let user_data = Borrow::<GlesRenderer>::borrow(renderer.glow_renderer())
.egl_context()
.user_data();
match user_data.get::<IndicatorElement>() {
Some(elem) => {
let mut elem = elem.0.borrow_mut();
if elem.geometry(1.0.into()).to_logical(1) != geo {
elem.resize(geo, None);
}
elem.clone()
}
None => {
let shader = Self::get(renderer);
user_data.insert_if_missing(|| IndicatorCache::new(HashMap::new()));
let mut cache = user_data.get::<IndicatorCache>().unwrap().borrow_mut();
cache.retain(|k, _| match k {
Key::Group(w) => w.upgrade().is_some(),
Key::Window(w) => w.alive(),
});
let elem = PixelShaderElement::new(
shader,
geo,
None, //TODO
alpha,
vec![
Uniform::new("color", FOCUS_INDICATOR_COLOR),
Uniform::new("thickness", thickness),
Uniform::new("radius", thickness * 2.0),
],
);
if !user_data.insert_if_missing(|| IndicatorElement(RefCell::new(elem.clone()))) {
*user_data.get::<IndicatorElement>().unwrap().0.borrow_mut() = elem.clone();
}
elem
}
let key = key.into();
if cache
.get(&key)
.filter(|(old_settings, _)| &settings == old_settings)
.is_none()
{
let thickness: f32 = thickness as f32;
let thickness_loc = (thickness as i32, thickness as i32);
let thickness_size = ((thickness * 2.0) as i32, (thickness * 2.0) as i32);
let geo = Rectangle::from_loc_and_size(
geo.loc - Point::from(thickness_loc),
geo.size + Size::from(thickness_size),
);
let shader = Self::get(renderer);
let elem = PixelShaderElement::new(
shader,
geo,
None, //TODO
alpha,
vec![
Uniform::new("color", color),
Uniform::new("thickness", thickness),
Uniform::new("radius", thickness * 2.0),
],
);
cache.insert(key.clone(), (settings, elem));
}
let elem = &mut cache.get_mut(&key).unwrap().1;
if elem.geometry(1.0.into()).to_logical(1) != geo {
elem.resize(geo, None);
}
elem.clone()
}
}
@ -307,6 +365,7 @@ where
&state.shell.override_redirect_windows,
state.xwayland_state.as_mut(),
(!move_active && is_active_space).then_some(&last_active_seat),
true,
state.config.static_conf.active_hint,
exclude_workspace_overview,
)

View file

@ -12,8 +12,8 @@ use smithay::{
input::KeyState,
renderer::{
element::{
utils::CropRenderElement, AsRenderElements, Element, RenderElement,
UnderlyingStorage,
utils::{CropRenderElement, RelocateRenderElement, RescaleRenderElement},
AsRenderElements, Element, RenderElement, UnderlyingStorage,
},
gles::element::PixelShaderElement,
glow::GlowRenderer,
@ -259,7 +259,7 @@ impl CosmicMapped {
}
pub fn set_tiled(&self, tiled: bool) {
for window in match &self.element {
if let Some(window) = match &self.element {
// we use the tiled state of stack windows anyway to get rid of decorations
CosmicMappedInternal::Stack(_) => None,
CosmicMappedInternal::Window(w) => Some(w.surface()),
@ -681,8 +681,16 @@ where
{
Stack(self::stack::CosmicStackRenderElement<R>),
Window(self::window::CosmicWindowRenderElement<R>),
CroppedStack(CropRenderElement<self::stack::CosmicStackRenderElement<R>>),
CroppedWindow(CropRenderElement<self::window::CosmicWindowRenderElement<R>>),
TiledStack(
RelocateRenderElement<
RescaleRenderElement<CropRenderElement<self::stack::CosmicStackRenderElement<R>>>,
>,
),
TiledWindow(
RelocateRenderElement<
RescaleRenderElement<CropRenderElement<self::window::CosmicWindowRenderElement<R>>>,
>,
),
Indicator(PixelShaderElement),
#[cfg(feature = "debug")]
Egui(TextureRenderElement<GlesTexture>),
@ -697,8 +705,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.id(),
CosmicMappedRenderElement::Window(elem) => elem.id(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.id(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.id(),
CosmicMappedRenderElement::TiledStack(elem) => elem.id(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.id(),
CosmicMappedRenderElement::Indicator(elem) => elem.id(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.id(),
@ -709,8 +717,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.current_commit(),
CosmicMappedRenderElement::Window(elem) => elem.current_commit(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.current_commit(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.current_commit(),
CosmicMappedRenderElement::TiledStack(elem) => elem.current_commit(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.current_commit(),
CosmicMappedRenderElement::Indicator(elem) => elem.current_commit(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.current_commit(),
@ -721,8 +729,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.src(),
CosmicMappedRenderElement::Window(elem) => elem.src(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.src(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.src(),
CosmicMappedRenderElement::TiledStack(elem) => elem.src(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.src(),
CosmicMappedRenderElement::Indicator(elem) => elem.src(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.src(),
@ -733,8 +741,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Window(elem) => elem.geometry(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.geometry(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.geometry(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.geometry(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.geometry(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.geometry(scale),
@ -745,8 +753,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.location(scale),
CosmicMappedRenderElement::Window(elem) => elem.location(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.location(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.location(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.location(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.location(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.location(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.location(scale),
@ -757,8 +765,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.transform(),
CosmicMappedRenderElement::Window(elem) => elem.transform(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.transform(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.transform(),
CosmicMappedRenderElement::TiledStack(elem) => elem.transform(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.transform(),
CosmicMappedRenderElement::Indicator(elem) => elem.transform(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.transform(),
@ -773,8 +781,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Window(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::CroppedStack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::TiledStack(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::TiledWindow(elem) => elem.damage_since(scale, commit),
CosmicMappedRenderElement::Indicator(elem) => elem.damage_since(scale, commit),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.damage_since(scale, commit),
@ -785,8 +793,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Window(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::CroppedStack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::TiledStack(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::TiledWindow(elem) => elem.opaque_regions(scale),
CosmicMappedRenderElement::Indicator(elem) => elem.opaque_regions(scale),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.opaque_regions(scale),
@ -797,8 +805,8 @@ where
match self {
CosmicMappedRenderElement::Stack(elem) => elem.alpha(),
CosmicMappedRenderElement::Window(elem) => elem.alpha(),
CosmicMappedRenderElement::CroppedStack(elem) => elem.alpha(),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.alpha(),
CosmicMappedRenderElement::TiledStack(elem) => elem.alpha(),
CosmicMappedRenderElement::TiledWindow(elem) => elem.alpha(),
CosmicMappedRenderElement::Indicator(elem) => elem.alpha(),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.alpha(),
@ -817,8 +825,8 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame, src, dst, damage)
}
@ -833,8 +841,8 @@ impl RenderElement<GlowRenderer> for CosmicMappedRenderElement<GlowRenderer> {
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => elem.underlying_storage(renderer),
#[cfg(feature = "debug")]
CosmicMappedRenderElement::Egui(elem) => elem.underlying_storage(renderer),
@ -855,8 +863,8 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Window(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledStack(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::TiledWindow(elem) => elem.draw(frame, src, dst, damage),
CosmicMappedRenderElement::Indicator(elem) => {
RenderElement::<GlowRenderer>::draw(elem, frame.glow_frame_mut(), src, dst, damage)
.map_err(|err| MultiError::Render(err))
@ -877,8 +885,8 @@ impl<'a, 'b> RenderElement<GlMultiRenderer<'a, 'b>>
match self {
CosmicMappedRenderElement::Stack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Window(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::CroppedWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledStack(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::TiledWindow(elem) => elem.underlying_storage(renderer),
CosmicMappedRenderElement::Indicator(elem) => {
elem.underlying_storage(renderer.glow_renderer_mut())
}

View file

@ -52,9 +52,9 @@ impl From<KeyboardFocusTarget> for PointerFocusTarget {
#[derive(Debug, Clone)]
pub struct WindowGroup {
pub(in crate::shell) node: NodeId,
pub(in crate::shell) output: WeakOutput,
pub(in crate::shell) alive: Weak<()>,
pub node: NodeId,
pub output: WeakOutput,
pub alive: Weak<()>,
}
impl PartialEq for WindowGroup {

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::{element::AsGlowRenderer, IndicatorShader},
backend::render::{element::AsGlowRenderer, IndicatorShader, FOCUS_INDICATOR_COLOR},
shell::{
element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement},
focus::target::{KeyboardFocusTarget, PointerFocusTarget},
@ -66,9 +66,11 @@ impl MoveGrabState {
elements.push(
CosmicMappedRenderElement::from(IndicatorShader::element(
renderer,
self.window.clone(),
Rectangle::from_loc_and_size(render_location, self.window.geometry().size),
self.indicator_thickness,
1.0,
FOCUS_INDICATOR_COLOR,
))
.into(),
);

View file

@ -13,7 +13,7 @@ use smithay::{
use std::collections::HashMap;
use crate::{
backend::render::{element::AsGlowRenderer, IndicatorShader},
backend::render::{element::AsGlowRenderer, IndicatorShader, FOCUS_INDICATOR_COLOR},
shell::{
element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement},
grabs::ResizeEdge,
@ -380,12 +380,14 @@ impl FloatingLayout {
if indicator_thickness > 0 {
let element = IndicatorShader::element(
renderer,
elem.clone(),
Rectangle::from_loc_and_size(
self.space.element_location(elem).unwrap() - output_loc,
elem.geometry().size,
),
indicator_thickness,
1.0,
FOCUS_INDICATOR_COLOR,
);
elements.insert(0, element.into());
}

View file

@ -1,7 +1,10 @@
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render::{element::AsGlowRenderer, IndicatorShader},
backend::render::{
element::AsGlowRenderer, IndicatorShader, Key, ACTIVE_GROUP_COLOR, FOCUS_INDICATOR_COLOR,
GROUP_COLOR,
},
shell::{
element::{window::CosmicWindowRenderElement, CosmicMapped, CosmicMappedRenderElement},
focus::{
@ -23,7 +26,10 @@ use cosmic_time::{Cubic, Ease, Tween};
use id_tree::{InsertBehavior, MoveBehavior, Node, NodeId, NodeIdError, RemoveBehavior, Tree};
use smithay::{
backend::renderer::{
element::{utils::CropRenderElement, AsRenderElements, RenderElement},
element::{
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
AsRenderElements, RenderElement,
},
ImportAll, ImportMem, Renderer,
},
desktop::{layer_map_for_output, space::SpaceElement, PopupKind},
@ -1432,6 +1438,8 @@ impl TilingLayout {
renderer: &mut R,
output: &Output,
focused: Option<&CosmicMapped>,
non_exclusive_zone: Rectangle<i32, Logical>,
draw_groups: bool,
indicator_thickness: u8,
) -> Result<Vec<CosmicMappedRenderElement<R>>, OutputNotMapped>
where
@ -1473,213 +1481,589 @@ impl TilingLayout {
let mut elements = Vec::new();
// all old windows and fade them out
if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
elements.extend(
reference_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry),
_ => unreachable!(),
})
.filter(|(mapped, _)| {
if let Some(root) = target_tree.root_node_id() {
!target_tree
.traverse_pre_order(root)
.unwrap()
.any(|node| node.data().is_mapped(Some(mapped)))
} else {
true
}
})
.flat_map(|(mapped, geo)| {
let crop_rect = geo.clone();
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
1.0 - percentage,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedStack)
}
CosmicMappedRenderElement::Window(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedWindow)
}
x => Some(x),
})
.collect::<Vec<_>>()
}),
// all gone windows and fade them out
let old_geometries = if let Some(reference_tree) = reference_tree.as_ref() {
let (geometries, _) = if draw_groups {
geometries_for_groupview(
reference_tree,
renderer,
non_exclusive_zone,
focused, // TODO: Would be better to be an old focus,
// but for that we have to associate focus with a tree (and animate focus changes properly)
1.0 - percentage,
)
} else {
None
}
}
.unzip();
if let Some(root) = target_tree.root_node_id() {
elements.extend(
target_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.filter(|node| match node.data() {
Data::Mapped { mapped, .. } => mapped.is_activated(),
_ => unreachable!(),
})
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry),
_ => unreachable!(),
})
.chain(
target_tree
.traverse_pre_order(root)
.unwrap()
.filter(|node| node.data().is_mapped(None))
.filter(|node| match node.data() {
Data::Mapped { mapped, .. } => !mapped.is_activated(),
_ => unreachable!(),
})
.map(|node| match node.data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry),
_ => unreachable!(),
}),
)
.flat_map(|(mapped, new_geo)| {
let old_geo = if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
reference_tree
.traverse_pre_order(root)
.unwrap()
.find(|node| node.data().is_mapped(Some(mapped)))
.map(|node| match node.data() {
Data::Mapped { last_geometry, .. } => last_geometry,
_ => unreachable!(),
})
} else {
None
}
} else {
None
};
// all old windows we want to fade out
elements.extend(render_old_tree(
reference_tree,
target_tree,
renderer,
geometries.clone(),
output_scale,
percentage,
));
let (geo, alpha) = if let Some(old_geo) = old_geo {
(
Rectangle::from_loc_and_size(
(
old_geo.loc.x
+ ((new_geo.loc.x - old_geo.loc.x) as f32 * percentage)
.round()
as i32,
old_geo.loc.y
+ ((new_geo.loc.y - old_geo.loc.y) as f32 * percentage)
.round()
as i32,
),
(
old_geo.size.w
+ ((new_geo.size.w - old_geo.size.w) as f32
* percentage)
.round()
as i32,
old_geo.size.h
+ ((new_geo.size.h - old_geo.size.h) as f32
* percentage)
.round()
as i32,
),
),
1.0,
)
} else {
// TODO: If old_geo.is_none() animate alpha - fade in
(*new_geo, percentage)
};
geometries
} else {
None
};
if alpha < 1.0 {
dbg!(alpha);
}
let crop_rect = geo.clone();
let mut elements =
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale),
Scale::from(output_scale),
alpha,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedStack)
}
CosmicMappedRenderElement::Window(elem) => {
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)
.map(CosmicMappedRenderElement::CroppedWindow)
}
x => Some(x),
})
.collect::<Vec<_>>();
if focused == Some(mapped) {
if indicator_thickness > 0 {
let element = IndicatorShader::element(
renderer,
geo,
indicator_thickness,
1.0,
);
elements.insert(0, element.into());
}
}
elements
}),
let (geometries, group_elements) = if draw_groups {
geometries_for_groupview(
target_tree,
renderer,
non_exclusive_zone,
focused,
percentage,
)
} else {
None
}
.unzip();
// tiling hints
if let Some(group_elements) = group_elements {
elements.extend(group_elements);
}
// all alive windows
elements.extend(render_new_tree(
target_tree,
reference_tree,
renderer,
geometries,
old_geometries,
focused,
output_scale,
percentage,
if draw_groups { 3 } else { indicator_thickness },
));
Ok(elements)
}
}
fn geometries_for_groupview<R>(
tree: &Tree<Data>,
renderer: &mut R,
non_exclusive_zone: Rectangle<i32, Logical>,
focused: Option<&CosmicMapped>,
alpha: f32,
) -> Option<(
HashMap<NodeId, Rectangle<i32, Logical>>,
Vec<CosmicMappedRenderElement<R>>,
)>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
{
// we need to recalculate geometry for all elements, if we are drawing groups
if let Some(root) = tree.root_node_id() {
let mut stack = vec![non_exclusive_zone];
let mut elements = Vec::new();
let mut geometries = HashMap::new();
const GAP: i32 = 16;
for node_id in tree.traverse_pre_order_ids(root).unwrap() {
if let Some(mut geo) = stack.pop() {
// zoom in windows
geo.loc += (GAP, GAP).into();
geo.size -= (GAP * 2, GAP * 2).into();
let node: &Node<Data> = tree.get(&node_id).unwrap();
let data = node.data();
let is_potential_group = if let Some(focused) = focused {
// 1. focused can move into us directly
if let Some(parent) = node.parent() {
let parent_data = tree.get(parent).unwrap().data();
let idx = tree
.children_ids(parent)
.unwrap()
.position(|id| id == &node_id)
.unwrap();
if let Some((focused_idx, _focused_id)) = tree
.children_ids(parent)
.unwrap()
.enumerate()
.find(|(_, child_id)| {
tree.get(child_id).unwrap().data().is_mapped(Some(focused))
})
{
// only direct neighbors
focused_idx.abs_diff(idx) == 1
// skip neighbors, if this is a group of two windows
&& !(parent_data.len() == 2 && data.is_mapped(None))
// skip groups of two in opposite orientation to indicate move between
&& !(parent_data.len() == 2 && if data.is_group() { parent_data.orientation() != data.orientation() } else { false } )
} else {
false
}
}
// 2. focused can move out into us
else {
tree.children_ids(&node_id)
.unwrap()
.find(|child_id| {
tree.children(child_id)
.unwrap()
.any(|child| child.data().is_mapped(Some(focused)))
})
.is_some()
}
} else {
false
};
match data {
Data::Group {
orientation,
last_geometry,
sizes,
alive,
} => {
let has_active_child = if let Some(focused) = focused {
tree.children(&node_id)
.unwrap()
.any(|child| child.data().is_mapped(Some(focused)))
} else {
false
};
if (is_potential_group || has_active_child) && &node_id != root {
elements.push(
IndicatorShader::element(
renderer,
Key::Group(Arc::downgrade(&alive)),
geo,
3,
alpha,
if has_active_child {
ACTIVE_GROUP_COLOR
} else {
GROUP_COLOR
},
)
.into(),
)
}
geometries.insert(node_id.clone(), geo);
let previous_length = match orientation {
Orientation::Horizontal => last_geometry.size.h,
Orientation::Vertical => last_geometry.size.w,
};
let new_length = match orientation {
Orientation::Horizontal => geo.size.h,
Orientation::Vertical => geo.size.w,
};
let mut sizes = sizes
.iter()
.map(|len| {
(((*len as f64) / (previous_length as f64)) * (new_length as f64))
.round() as i32
})
.collect::<Vec<_>>();
let sum: i32 = sizes.iter().sum();
if sum < new_length {
*sizes.last_mut().unwrap() += new_length - sum;
}
match orientation {
Orientation::Horizontal => {
let mut previous: i32 = sizes.iter().sum();
for size in sizes.iter().rev() {
previous -= *size;
stack.push(Rectangle::from_loc_and_size(
(geo.loc.x, geo.loc.y + previous),
(geo.size.w, *size),
));
}
}
Orientation::Vertical => {
let mut previous: i32 = sizes.iter().sum();
for size in sizes.iter().rev() {
previous -= *size;
stack.push(Rectangle::from_loc_and_size(
(geo.loc.x + previous, geo.loc.y),
(*size, geo.size.h),
));
}
}
}
}
Data::Mapped { mapped, .. } => {
if is_potential_group {
elements.push(
IndicatorShader::element(
renderer,
mapped.clone(),
geo,
3,
alpha,
GROUP_COLOR,
)
.into(),
);
geo.loc += (GAP, GAP).into();
geo.size -= (GAP * 2, GAP * 2).into();
}
geometries.insert(node_id.clone(), geo);
}
}
}
}
Some((geometries, elements))
} else {
None
}
}
fn render_old_tree<R>(
reference_tree: &Tree<Data>,
target_tree: &Tree<Data>,
renderer: &mut R,
geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
output_scale: f64,
percentage: f32,
) -> Vec<CosmicMappedRenderElement<R>>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
{
if let Some(root) = reference_tree.root_node_id() {
let geometries = geometries.unwrap_or_default();
reference_tree
.traverse_pre_order_ids(root)
.unwrap()
.filter(|node_id| reference_tree.get(node_id).unwrap().data().is_mapped(None))
.map(
|node_id| match reference_tree.get(&node_id).unwrap().data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry, geometries.get(&node_id)),
_ => unreachable!(),
},
)
.filter(|(mapped, _, _)| {
if let Some(root) = target_tree.root_node_id() {
!target_tree
.traverse_pre_order(root)
.unwrap()
.any(|node| node.data().is_mapped(Some(mapped)))
} else {
true
}
})
.flat_map(|(mapped, original_geo, scaled_geo)| {
let (scale, offset) = scaled_geo
.map(|adapted_geo| scale_to_center(&original_geo, adapted_geo))
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
let geo = scaled_geo
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
)
})
.unwrap_or(*original_geo);
let crop_rect = geo.clone();
let original_location = original_geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale);
AsRenderElements::<R>::render_elements::<CosmicMappedRenderElement<R>>(
mapped,
renderer,
original_location,
Scale::from(output_scale),
1.0 - percentage,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => Some(
CosmicMappedRenderElement::TiledStack(RelocateRenderElement::from_element(
RescaleRenderElement::from_element(
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)?,
original_location,
scale,
),
geo.loc.to_physical_precise_round(output_scale),
Relocate::Absolute,
)),
),
CosmicMappedRenderElement::Window(elem) => {
Some(CosmicMappedRenderElement::TiledWindow(
RelocateRenderElement::from_element(
RescaleRenderElement::from_element(
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)?,
(0, 0).into(),
scale,
),
geo.loc.to_physical_precise_round(output_scale),
Relocate::Absolute,
),
))
}
x => Some(x),
})
.collect::<Vec<_>>()
})
.collect()
} else {
Vec::new()
}
}
fn render_new_tree<R>(
target_tree: &Tree<Data>,
reference_tree: Option<&Tree<Data>>,
renderer: &mut R,
geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
old_geometries: Option<HashMap<NodeId, Rectangle<i32, Logical>>>,
focused: Option<&CosmicMapped>,
output_scale: f64,
percentage: f32,
indicator_thickness: u8,
) -> Vec<CosmicMappedRenderElement<R>>
where
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
<R as Renderer>::TextureId: 'static,
CosmicMappedRenderElement<R>: RenderElement<R>,
CosmicWindowRenderElement<R>: RenderElement<R>,
{
if let Some(root) = target_tree.root_node_id() {
let old_geometries = old_geometries.unwrap_or_default();
let geometries = geometries.unwrap_or_default();
target_tree
.traverse_pre_order_ids(root)
.unwrap()
.filter(|node_id| target_tree.get(node_id).unwrap().data().is_mapped(None))
.map(|node_id| match target_tree.get(&node_id).unwrap().data() {
Data::Mapped {
mapped,
last_geometry,
..
} => (mapped, last_geometry, geometries.get(&node_id)),
_ => unreachable!(),
})
.flat_map(|(mapped, original_geo, scaled_geo)| {
let (old_original_geo, old_scaled_geo) =
if let Some(reference_tree) = reference_tree.as_ref() {
if let Some(root) = reference_tree.root_node_id() {
reference_tree
.traverse_pre_order_ids(root)
.unwrap()
.find(|node_id| {
reference_tree
.get(node_id)
.unwrap()
.data()
.is_mapped(Some(mapped))
})
.map(
|node_id| match reference_tree.get(&node_id).unwrap().data() {
Data::Mapped { last_geometry, .. } => {
(last_geometry, old_geometries.get(&node_id))
}
_ => unreachable!(),
},
)
} else {
None
}
} else {
None
}
.unzip();
let old_geo = old_original_geo.map(|original_geo| {
let (scale, offset) = old_scaled_geo
.unwrap()
.map(|adapted_geo| scale_to_center(original_geo, adapted_geo))
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
old_scaled_geo
.unwrap()
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
)
})
.unwrap_or(*original_geo)
});
let crop_rect = original_geo;
let (scale, offset) = scaled_geo
.map(|adapted_geo| scale_to_center(original_geo, adapted_geo))
.unwrap_or_else(|| (1.0.into(), (0, 0).into()));
let new_geo = scaled_geo
.map(|adapted_geo| {
Rectangle::from_loc_and_size(
adapted_geo.loc + offset,
(
(original_geo.size.w as f64 * scale).round() as i32,
(original_geo.size.h as f64 * scale).round() as i32,
),
)
})
.unwrap_or(*original_geo);
let (geo, alpha) = if let Some(old_geo) = old_geo {
(
Rectangle::from_loc_and_size(
(
old_geo.loc.x
+ ((new_geo.loc.x - old_geo.loc.x) as f32 * percentage).round()
as i32,
old_geo.loc.y
+ ((new_geo.loc.y - old_geo.loc.y) as f32 * percentage).round()
as i32,
),
(
old_geo.size.w
+ ((new_geo.size.w - old_geo.size.w) as f32 * percentage)
.round() as i32,
old_geo.size.h
+ ((new_geo.size.h - old_geo.size.h) as f32 * percentage)
.round() as i32,
),
),
1.0,
)
} else {
(new_geo, percentage)
};
let original_location = original_geo.loc.to_physical_precise_round(output_scale)
- mapped
.geometry()
.loc
.to_physical_precise_round(output_scale);
let mut elements = AsRenderElements::<R>::render_elements::<
CosmicMappedRenderElement<R>,
>(
mapped,
renderer,
original_location,
Scale::from(output_scale),
alpha,
)
.into_iter()
.flat_map(|element| match element {
CosmicMappedRenderElement::Stack(elem) => Some(
CosmicMappedRenderElement::TiledStack(RelocateRenderElement::from_element(
RescaleRenderElement::from_element(
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)?,
original_location,
scale,
),
geo.loc.to_physical_precise_round(output_scale),
Relocate::Absolute,
)),
),
CosmicMappedRenderElement::Window(elem) => {
Some(CosmicMappedRenderElement::TiledWindow(
RelocateRenderElement::from_element(
RescaleRenderElement::from_element(
CropRenderElement::from_element(
elem,
output_scale,
crop_rect.to_physical_precise_round(output_scale),
)?,
(0, 0).into(),
scale,
),
geo.loc.to_physical_precise_round(output_scale),
Relocate::Absolute,
),
))
}
x => Some(x),
})
.collect::<Vec<_>>();
if focused == Some(mapped) {
if indicator_thickness > 0 {
let element = IndicatorShader::element(
renderer,
mapped.clone(),
geo,
indicator_thickness,
1.0,
FOCUS_INDICATOR_COLOR,
);
elements.insert(0, element.into());
}
}
elements
})
.collect()
} else {
Vec::new()
}
}
fn scale_to_center(
old_geo: &Rectangle<i32, Logical>,
new_geo: &Rectangle<i32, Logical>,
) -> (f64, Point<i32, Logical>) {
let scale_w = new_geo.size.w as f64 / old_geo.size.w as f64;
let scale_h = new_geo.size.h as f64 / old_geo.size.h as f64;
if scale_w > scale_h {
(
scale_h,
(
((new_geo.size.w as f64 - old_geo.size.w as f64 * scale_h) / 2.0).round() as i32,
0,
)
.into(),
)
} else {
(
scale_w,
(
0,
((new_geo.size.h as f64 - old_geo.size.h as f64 * scale_w) / 2.0).round() as i32,
)
.into(),
)
}
}

View file

@ -454,6 +454,7 @@ impl Workspace {
override_redirect_windows: &[X11Surface],
xwm_state: Option<&'a mut XWaylandState>,
draw_focus_indicator: Option<&Seat<State>>,
draw_groups: bool,
indicator_thickness: u8,
exclude_workspace_overview: bool,
) -> Result<Vec<WorkspaceRenderElement<R>>, OutputNotMapped>
@ -604,7 +605,14 @@ impl Workspace {
//tiling surfaces
render_elements.extend(
self.tiling_layer
.render_output::<R>(renderer, output, focused.as_ref(), indicator_thickness)?
.render_output::<R>(
renderer,
output,
focused.as_ref(),
layer_map.non_exclusive_zone(),
draw_groups,
indicator_thickness,
)?
.into_iter()
.map(WorkspaceRenderElement::from),
);