shell: Add zoom UI
This commit is contained in:
parent
55e4dd7c0f
commit
f72d2b91f3
10 changed files with 1247 additions and 261 deletions
|
|
@ -3,6 +3,7 @@ use crate::shell::{CosmicMappedRenderElement, WorkspaceRenderElement};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::renderer::{
|
||||||
element::{
|
element::{
|
||||||
|
memory::MemoryRenderBufferRenderElement,
|
||||||
surface::WaylandSurfaceRenderElement,
|
surface::WaylandSurfaceRenderElement,
|
||||||
texture::TextureRenderElement,
|
texture::TextureRenderElement,
|
||||||
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
|
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement},
|
||||||
|
|
@ -36,6 +37,7 @@ where
|
||||||
RelocateRenderElement<RescaleRenderElement<TextureRenderElement<GlesTexture>>>,
|
RelocateRenderElement<RescaleRenderElement<TextureRenderElement<GlesTexture>>>,
|
||||||
>,
|
>,
|
||||||
),
|
),
|
||||||
|
Zoom(MemoryRenderBufferRenderElement<R>),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
Egui(TextureRenderElement<GlesTexture>),
|
Egui(TextureRenderElement<GlesTexture>),
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +56,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.id(),
|
CosmicElement::MoveGrab(elem) => elem.id(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.id(),
|
CosmicElement::AdditionalDamage(elem) => elem.id(),
|
||||||
CosmicElement::Mirror(elem) => elem.id(),
|
CosmicElement::Mirror(elem) => elem.id(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.id(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.id(),
|
CosmicElement::Egui(elem) => elem.id(),
|
||||||
}
|
}
|
||||||
|
|
@ -67,6 +70,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
CosmicElement::MoveGrab(elem) => elem.current_commit(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.current_commit(),
|
CosmicElement::AdditionalDamage(elem) => elem.current_commit(),
|
||||||
CosmicElement::Mirror(elem) => elem.current_commit(),
|
CosmicElement::Mirror(elem) => elem.current_commit(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.current_commit(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.current_commit(),
|
CosmicElement::Egui(elem) => elem.current_commit(),
|
||||||
}
|
}
|
||||||
|
|
@ -80,6 +84,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.src(),
|
CosmicElement::MoveGrab(elem) => elem.src(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.src(),
|
CosmicElement::AdditionalDamage(elem) => elem.src(),
|
||||||
CosmicElement::Mirror(elem) => elem.src(),
|
CosmicElement::Mirror(elem) => elem.src(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.src(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.src(),
|
CosmicElement::Egui(elem) => elem.src(),
|
||||||
}
|
}
|
||||||
|
|
@ -93,6 +98,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
CosmicElement::MoveGrab(elem) => elem.geometry(scale),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.geometry(scale),
|
CosmicElement::AdditionalDamage(elem) => elem.geometry(scale),
|
||||||
CosmicElement::Mirror(elem) => elem.geometry(scale),
|
CosmicElement::Mirror(elem) => elem.geometry(scale),
|
||||||
|
CosmicElement::Zoom(elem) => elem.geometry(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.geometry(scale),
|
CosmicElement::Egui(elem) => elem.geometry(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +112,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
CosmicElement::MoveGrab(elem) => elem.location(scale),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.location(scale),
|
CosmicElement::AdditionalDamage(elem) => elem.location(scale),
|
||||||
CosmicElement::Mirror(elem) => elem.location(scale),
|
CosmicElement::Mirror(elem) => elem.location(scale),
|
||||||
|
CosmicElement::Zoom(elem) => elem.location(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.location(scale),
|
CosmicElement::Egui(elem) => elem.location(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -119,6 +126,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.transform(),
|
CosmicElement::MoveGrab(elem) => elem.transform(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.transform(),
|
CosmicElement::AdditionalDamage(elem) => elem.transform(),
|
||||||
CosmicElement::Mirror(elem) => elem.transform(),
|
CosmicElement::Mirror(elem) => elem.transform(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.transform(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.transform(),
|
CosmicElement::Egui(elem) => elem.transform(),
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +144,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
CosmicElement::MoveGrab(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit),
|
CosmicElement::AdditionalDamage(elem) => elem.damage_since(scale, commit),
|
||||||
CosmicElement::Mirror(elem) => elem.damage_since(scale, commit),
|
CosmicElement::Mirror(elem) => elem.damage_since(scale, commit),
|
||||||
|
CosmicElement::Zoom(elem) => elem.damage_since(scale, commit),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
CosmicElement::Egui(elem) => elem.damage_since(scale, commit),
|
||||||
}
|
}
|
||||||
|
|
@ -149,6 +158,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
CosmicElement::MoveGrab(elem) => elem.opaque_regions(scale),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale),
|
CosmicElement::AdditionalDamage(elem) => elem.opaque_regions(scale),
|
||||||
CosmicElement::Mirror(elem) => elem.opaque_regions(scale),
|
CosmicElement::Mirror(elem) => elem.opaque_regions(scale),
|
||||||
|
CosmicElement::Zoom(elem) => elem.opaque_regions(scale),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
CosmicElement::Egui(elem) => elem.opaque_regions(scale),
|
||||||
}
|
}
|
||||||
|
|
@ -162,6 +172,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
CosmicElement::MoveGrab(elem) => elem.alpha(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.alpha(),
|
CosmicElement::AdditionalDamage(elem) => elem.alpha(),
|
||||||
CosmicElement::Mirror(elem) => elem.alpha(),
|
CosmicElement::Mirror(elem) => elem.alpha(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.alpha(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.alpha(),
|
CosmicElement::Egui(elem) => elem.alpha(),
|
||||||
}
|
}
|
||||||
|
|
@ -175,6 +186,7 @@ where
|
||||||
CosmicElement::MoveGrab(elem) => elem.kind(),
|
CosmicElement::MoveGrab(elem) => elem.kind(),
|
||||||
CosmicElement::AdditionalDamage(elem) => elem.kind(),
|
CosmicElement::AdditionalDamage(elem) => elem.kind(),
|
||||||
CosmicElement::Mirror(elem) => elem.kind(),
|
CosmicElement::Mirror(elem) => elem.kind(),
|
||||||
|
CosmicElement::Zoom(elem) => elem.kind(),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => elem.kind(),
|
CosmicElement::Egui(elem) => elem.kind(),
|
||||||
}
|
}
|
||||||
|
|
@ -219,6 +231,7 @@ where
|
||||||
};
|
};
|
||||||
elem
|
elem
|
||||||
}
|
}
|
||||||
|
CosmicElement::Zoom(elem) => elem.draw(frame, src, dst, damage, opaque_regions),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => {
|
CosmicElement::Egui(elem) => {
|
||||||
let elem = {
|
let elem = {
|
||||||
|
|
@ -254,6 +267,7 @@ where
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CosmicElement::Zoom(elem) => elem.underlying_storage(renderer),
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
CosmicElement::Egui(elem) => {
|
CosmicElement::Egui(elem) => {
|
||||||
let glow_renderer = renderer.glow_renderer_mut();
|
let glow_renderer = renderer.glow_renderer_mut();
|
||||||
|
|
@ -295,6 +309,17 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<R> From<MemoryRenderBufferRenderElement<R>> for CosmicElement<R>
|
||||||
|
where
|
||||||
|
R: Renderer + ImportAll + ImportMem + AsGlowRenderer,
|
||||||
|
<R as Renderer>::TextureId: 'static,
|
||||||
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
|
{
|
||||||
|
fn from(value: MemoryRenderBufferRenderElement<R>) -> Self {
|
||||||
|
Self::Zoom(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
|
impl<R> From<TextureRenderElement<GlesTexture>> for CosmicElement<R>
|
||||||
where
|
where
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use crate::{
|
||||||
focus::{render_input_order, target::WindowGroup, Stage},
|
focus::{render_input_order, target::WindowGroup, Stage},
|
||||||
grabs::{SeatMenuGrabState, SeatMoveGrabState},
|
grabs::{SeatMenuGrabState, SeatMoveGrabState},
|
||||||
layout::tiling::ANIMATION_DURATION,
|
layout::tiling::ANIMATION_DURATION,
|
||||||
|
zoom::ZoomState,
|
||||||
CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta,
|
CosmicMappedRenderElement, OverviewMode, SeatExt, Trigger, WorkspaceDelta,
|
||||||
WorkspaceRenderElement,
|
WorkspaceRenderElement,
|
||||||
},
|
},
|
||||||
|
|
@ -404,7 +405,7 @@ pub enum CursorMode {
|
||||||
pub fn cursor_elements<'a, 'frame, R>(
|
pub fn cursor_elements<'a, 'frame, R>(
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
seats: impl Iterator<Item = &'a Seat<State>>,
|
seats: impl Iterator<Item = &'a Seat<State>>,
|
||||||
zoom_level: Option<(Seat<State>, Point<f64, Global>, f64)>,
|
zoom_state: Option<&ZoomState>,
|
||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
now: Time<Monotonic>,
|
now: Time<Monotonic>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
|
|
@ -417,8 +418,13 @@ where
|
||||||
CosmicMappedRenderElement<R>: RenderElement<R>,
|
CosmicMappedRenderElement<R>: RenderElement<R>,
|
||||||
{
|
{
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
let (focal_point, zoom_scale) = zoom_level
|
let (focal_point, zoom_scale) = zoom_state
|
||||||
.map(|(_, p, s)| (p.to_local(&output), s))
|
.map(|state| {
|
||||||
|
(
|
||||||
|
state.focal_point(Some(&output)).to_local(&output),
|
||||||
|
state.animating_level(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
|
|
@ -495,23 +501,32 @@ where
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(grab_elements) = seat
|
if let Some((grab_elements, should_scale)) = seat
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<SeatMenuGrabState>()
|
.get::<SeatMenuGrabState>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|state| state.render::<CosmicMappedRenderElement<R>, R>(renderer, output))
|
.map(|state| {
|
||||||
|
(
|
||||||
|
state.render::<CosmicMappedRenderElement<R>, R>(renderer, output),
|
||||||
|
!state.is_in_screen_space(),
|
||||||
|
)
|
||||||
|
})
|
||||||
{
|
{
|
||||||
elements.extend(grab_elements.into_iter().map(|elem| {
|
elements.extend(grab_elements.into_iter().map(|elem| {
|
||||||
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
CosmicElement::MoveGrab(RescaleRenderElement::from_element(
|
||||||
elem,
|
elem,
|
||||||
focal_point
|
if should_scale {
|
||||||
.as_logical()
|
focal_point
|
||||||
.to_physical(output.current_scale().fractional_scale())
|
.as_logical()
|
||||||
.to_i32_round(),
|
.to_physical(output.current_scale().fractional_scale())
|
||||||
zoom_scale,
|
.to_i32_round()
|
||||||
|
} else {
|
||||||
|
Point::from((0, 0))
|
||||||
|
},
|
||||||
|
if should_scale { zoom_scale } else { 1.0 },
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -595,14 +610,14 @@ where
|
||||||
} else {
|
} else {
|
||||||
ElementFilter::All
|
ElementFilter::All
|
||||||
};
|
};
|
||||||
let zoom_level = shell.read().unwrap().zoom_level(Some(&output));
|
let zoom_state = shell.read().unwrap().zoom_state().cloned();
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let workspace_elements = workspace_elements(
|
let workspace_elements = workspace_elements(
|
||||||
_gpu,
|
_gpu,
|
||||||
renderer,
|
renderer,
|
||||||
shell,
|
shell,
|
||||||
zoom_level,
|
zoom_state.as_ref(),
|
||||||
now,
|
now,
|
||||||
output,
|
output,
|
||||||
previous_workspace,
|
previous_workspace,
|
||||||
|
|
@ -625,7 +640,7 @@ pub fn workspace_elements<R>(
|
||||||
_gpu: Option<&DrmNode>,
|
_gpu: Option<&DrmNode>,
|
||||||
renderer: &mut R,
|
renderer: &mut R,
|
||||||
shell: &Arc<RwLock<Shell>>,
|
shell: &Arc<RwLock<Shell>>,
|
||||||
zoom_level: Option<(Seat<State>, Point<f64, Global>, f64)>,
|
zoom_level: Option<&ZoomState>,
|
||||||
now: Time<Monotonic>,
|
now: Time<Monotonic>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
|
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
|
||||||
|
|
@ -642,18 +657,16 @@ where
|
||||||
{
|
{
|
||||||
let mut elements = Vec::new();
|
let mut elements = Vec::new();
|
||||||
|
|
||||||
let theme = shell.read().unwrap().theme().clone();
|
let shell_ref = shell.read().unwrap();
|
||||||
let seats = shell
|
let seats = shell_ref.seats.iter().cloned().collect::<Vec<_>>();
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.seats
|
|
||||||
.iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if seats.is_empty() {
|
if seats.is_empty() {
|
||||||
return Ok(Vec::new());
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
let theme = shell_ref.theme().clone();
|
||||||
let scale = output.current_scale().fractional_scale();
|
let scale = output.current_scale().fractional_scale();
|
||||||
|
// we don't want to hold a shell lock across `cursor_elements`,
|
||||||
|
// that is prone to deadlock with the main-thread on some grabs.
|
||||||
|
std::mem::drop(shell_ref);
|
||||||
|
|
||||||
elements.extend(cursor_elements(
|
elements.extend(cursor_elements(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -667,7 +680,6 @@ where
|
||||||
));
|
));
|
||||||
|
|
||||||
let shell = shell.read().unwrap();
|
let shell = shell.read().unwrap();
|
||||||
|
|
||||||
let overview = shell.overview_mode();
|
let overview = shell.overview_mode();
|
||||||
let (resize_mode, resize_indicator) = shell.resize_mode();
|
let (resize_mode, resize_indicator) = shell.resize_mode();
|
||||||
let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator));
|
let resize_indicator = resize_indicator.map(|indicator| (resize_mode, indicator));
|
||||||
|
|
@ -715,7 +727,12 @@ where
|
||||||
.as_logical()
|
.as_logical()
|
||||||
.to_physical_precise_round(scale);
|
.to_physical_precise_round(scale);
|
||||||
let (focal_point, zoom_scale) = zoom_level
|
let (focal_point, zoom_scale) = zoom_level
|
||||||
.map(|(_, p, s)| (p.to_local(&output), s))
|
.map(|state| {
|
||||||
|
(
|
||||||
|
state.focal_point(Some(&output)).to_local(&output),
|
||||||
|
state.animating_level(),
|
||||||
|
)
|
||||||
|
})
|
||||||
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
.unwrap_or_else(|| ((0., 0.).into(), 1.));
|
||||||
|
|
||||||
let crop_to_output = |element: WorkspaceRenderElement<R>| {
|
let crop_to_output = |element: WorkspaceRenderElement<R>| {
|
||||||
|
|
@ -741,6 +758,9 @@ where
|
||||||
element_filter,
|
element_filter,
|
||||||
|stage| {
|
|stage| {
|
||||||
match stage {
|
match stage {
|
||||||
|
Stage::ZoomUI => {
|
||||||
|
elements.extend(ZoomState::render(renderer, output));
|
||||||
|
}
|
||||||
Stage::SessionLock(lock_surface) => {
|
Stage::SessionLock(lock_surface) => {
|
||||||
elements.extend(
|
elements.extend(
|
||||||
session_lock_elements(renderer, output, lock_surface)
|
session_lock_elements(renderer, output, lock_surface)
|
||||||
|
|
@ -1006,6 +1026,7 @@ where
|
||||||
.zip(previous_idx)
|
.zip(previous_idx)
|
||||||
.map(|((w, start), idx)| (w.handle, idx, start));
|
.map(|((w, start), idx)| (w.handle, idx, start));
|
||||||
let workspace = (workspace.handle, idx);
|
let workspace = (workspace.handle, idx);
|
||||||
|
let zoom_state = shell_ref.zoom_state().cloned();
|
||||||
std::mem::drop(shell_ref);
|
std::mem::drop(shell_ref);
|
||||||
|
|
||||||
let element_filter = if workspace_overview_is_open(output) {
|
let element_filter = if workspace_overview_is_open(output) {
|
||||||
|
|
@ -1013,7 +1034,6 @@ where
|
||||||
} else {
|
} else {
|
||||||
ElementFilter::All
|
ElementFilter::All
|
||||||
};
|
};
|
||||||
let zoom_level = shell.read().unwrap().zoom_level(Some(&output));
|
|
||||||
|
|
||||||
let result = render_workspace(
|
let result = render_workspace(
|
||||||
gpu,
|
gpu,
|
||||||
|
|
@ -1023,7 +1043,7 @@ where
|
||||||
age,
|
age,
|
||||||
None,
|
None,
|
||||||
shell,
|
shell,
|
||||||
zoom_level,
|
zoom_state.as_ref(),
|
||||||
now,
|
now,
|
||||||
output,
|
output,
|
||||||
previous_workspace,
|
previous_workspace,
|
||||||
|
|
@ -1136,7 +1156,7 @@ pub fn render_workspace<'d, R, Target, OffTarget>(
|
||||||
age: usize,
|
age: usize,
|
||||||
additional_damage: Option<Vec<Rectangle<i32, Logical>>>,
|
additional_damage: Option<Vec<Rectangle<i32, Logical>>>,
|
||||||
shell: &Arc<RwLock<Shell>>,
|
shell: &Arc<RwLock<Shell>>,
|
||||||
zoom_level: Option<(Seat<State>, Point<f64, Global>, f64)>,
|
zoom_level: Option<&ZoomState>,
|
||||||
now: Time<Monotonic>,
|
now: Time<Monotonic>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
|
previous: Option<(WorkspaceHandle, usize, WorkspaceDelta)>,
|
||||||
|
|
|
||||||
|
|
@ -323,6 +323,7 @@ fn format_pointer_focus(focus: Option<PointerFocusTarget>) -> String {
|
||||||
window.surface().title()
|
window.surface().title()
|
||||||
),
|
),
|
||||||
Some(ResizeFork(_)) => String::from("Resize UI"),
|
Some(ResizeFork(_)) => String::from("Resize UI"),
|
||||||
|
Some(ZoomUI(_)) => String::from("Zoom UI"),
|
||||||
None => format!("None"),
|
None => format!("None"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
floating::ResizeGrabMarker,
|
floating::ResizeGrabMarker,
|
||||||
tiling::{NodeDesc, SwapWindowGrab, TilingLayout},
|
tiling::{NodeDesc, SwapWindowGrab, TilingLayout},
|
||||||
},
|
},
|
||||||
|
zoom::ZoomState,
|
||||||
SeatExt, Trigger,
|
SeatExt, Trigger,
|
||||||
},
|
},
|
||||||
utils::{float::NextDown, prelude::*, quirks::workspace_overview_is_open},
|
utils::{float::NextDown, prelude::*, quirks::workspace_overview_is_open},
|
||||||
|
|
@ -1187,7 +1188,8 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = transform_output_mapped_position(&output, &event);
|
let position =
|
||||||
|
transform_output_mapped_position(&output, &event, shell.zoom_state());
|
||||||
let under = State::surface_under(position, &output, &mut *shell)
|
let under = State::surface_under(position, &output, &mut *shell)
|
||||||
.map(|(target, pos)| (target, pos.as_logical()));
|
.map(|(target, pos)| (target, pos.as_logical()));
|
||||||
|
|
||||||
|
|
@ -1218,7 +1220,8 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = transform_output_mapped_position(&output, &event);
|
let position =
|
||||||
|
transform_output_mapped_position(&output, &event, shell.zoom_state());
|
||||||
let under = State::surface_under(position, &output, &mut *shell)
|
let under = State::surface_under(position, &output, &mut *shell)
|
||||||
.map(|(target, pos)| (target, pos.as_logical()));
|
.map(|(target, pos)| (target, pos.as_logical()));
|
||||||
|
|
||||||
|
|
@ -1302,7 +1305,8 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = transform_output_mapped_position(&output, &event);
|
let position =
|
||||||
|
transform_output_mapped_position(&output, &event, shell.zoom_state());
|
||||||
let under = State::surface_under(position, &output, &mut *shell)
|
let under = State::surface_under(position, &output, &mut *shell)
|
||||||
.map(|(target, pos)| (target, pos.as_logical()));
|
.map(|(target, pos)| (target, pos.as_logical()));
|
||||||
|
|
||||||
|
|
@ -1366,7 +1370,8 @@ impl State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let position = transform_output_mapped_position(&output, &event);
|
let position =
|
||||||
|
transform_output_mapped_position(&output, &event, shell.zoom_state());
|
||||||
let under = State::surface_under(position, &output, &mut *shell)
|
let under = State::surface_under(position, &output, &mut *shell)
|
||||||
.map(|(target, pos)| (target, pos.as_logical()));
|
.map(|(target, pos)| (target, pos.as_logical()));
|
||||||
|
|
||||||
|
|
@ -1939,6 +1944,7 @@ impl State {
|
||||||
element_filter,
|
element_filter,
|
||||||
|stage| {
|
|stage| {
|
||||||
match stage {
|
match stage {
|
||||||
|
Stage::ZoomUI => {}
|
||||||
Stage::SessionLock(lock_surface) => {
|
Stage::SessionLock(lock_surface) => {
|
||||||
return ControlFlow::Break(Ok(lock_surface
|
return ControlFlow::Break(Ok(lock_surface
|
||||||
.cloned()
|
.cloned()
|
||||||
|
|
@ -2067,6 +2073,15 @@ impl State {
|
||||||
element_filter,
|
element_filter,
|
||||||
|stage| {
|
|stage| {
|
||||||
match stage {
|
match stage {
|
||||||
|
Stage::ZoomUI => {
|
||||||
|
if let Some(zoom_state) = shell.zoom_state() {
|
||||||
|
if let Some((target, loc)) =
|
||||||
|
zoom_state.surface_under(output, global_pos)
|
||||||
|
{
|
||||||
|
return ControlFlow::Break(Ok(Some((target, loc))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Stage::SessionLock(lock_surface) => {
|
Stage::SessionLock(lock_surface) => {
|
||||||
return ControlFlow::Break(Ok(lock_surface.map(|surface| {
|
return ControlFlow::Break(Ok(lock_surface.map(|surface| {
|
||||||
(
|
(
|
||||||
|
|
@ -2196,13 +2211,19 @@ fn cursor_sessions_for_output<'a>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_output_mapped_position<'a, B, E>(output: &Output, event: &E) -> Point<f64, Global>
|
fn transform_output_mapped_position<'a, B, E>(
|
||||||
|
output: &Output,
|
||||||
|
event: &E,
|
||||||
|
zoom_state: Option<&ZoomState>,
|
||||||
|
) -> Point<f64, Global>
|
||||||
where
|
where
|
||||||
B: InputBackend,
|
B: InputBackend,
|
||||||
E: AbsolutePositionEvent<B>,
|
E: AbsolutePositionEvent<B>,
|
||||||
B::Device: 'static,
|
B::Device: 'static,
|
||||||
{
|
{
|
||||||
let geometry = output.geometry();
|
let geometry = zoom_state
|
||||||
|
.and_then(|state| output.zoomed_geometry(state.current_level()))
|
||||||
|
.unwrap_or_else(|| output.geometry());
|
||||||
let transform = output.current_transform();
|
let transform = output.current_transform();
|
||||||
let size = transform
|
let size = transform
|
||||||
.invert()
|
.invert()
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub enum Stage<'a> {
|
pub enum Stage<'a> {
|
||||||
|
ZoomUI,
|
||||||
SessionLock(Option<&'a LockSurface>),
|
SessionLock(Option<&'a LockSurface>),
|
||||||
LayerPopup {
|
LayerPopup {
|
||||||
layer: LayerSurface,
|
layer: LayerSurface,
|
||||||
|
|
@ -69,6 +70,10 @@ fn render_input_order_internal<R: 'static>(
|
||||||
element_filter: ElementFilter,
|
element_filter: ElementFilter,
|
||||||
mut callback: impl FnMut(Stage) -> ControlFlow<Result<R, OutputNoMode>, ()>,
|
mut callback: impl FnMut(Stage) -> ControlFlow<Result<R, OutputNoMode>, ()>,
|
||||||
) -> ControlFlow<Result<R, OutputNoMode>, ()> {
|
) -> ControlFlow<Result<R, OutputNoMode>, ()> {
|
||||||
|
if shell.zoom_state.is_some() {
|
||||||
|
callback(Stage::ZoomUI)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Session Lock
|
// Session Lock
|
||||||
if let Some(session_lock) = &shell.session_lock {
|
if let Some(session_lock) = &shell.session_lock {
|
||||||
return callback(Stage::SessionLock(session_lock.surfaces.get(output)));
|
return callback(Stage::SessionLock(session_lock.surfaces.get(output)));
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
shell::{
|
shell::{
|
||||||
element::{CosmicMapped, CosmicStack, CosmicWindow},
|
element::{CosmicMapped, CosmicStack, CosmicWindow},
|
||||||
layout::tiling::ResizeForkTarget,
|
layout::tiling::ResizeForkTarget,
|
||||||
|
zoom::ZoomFocusTarget,
|
||||||
CosmicSurface, SeatExt,
|
CosmicSurface, SeatExt,
|
||||||
},
|
},
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
|
|
@ -41,6 +42,7 @@ pub enum PointerFocusTarget {
|
||||||
StackUI(CosmicStack),
|
StackUI(CosmicStack),
|
||||||
WindowUI(CosmicWindow),
|
WindowUI(CosmicWindow),
|
||||||
ResizeFork(ResizeForkTarget),
|
ResizeFork(ResizeForkTarget),
|
||||||
|
ZoomUI(ZoomFocusTarget),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
@ -188,6 +190,7 @@ impl IsAlive for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(e) => e.alive(),
|
PointerFocusTarget::StackUI(e) => e.alive(),
|
||||||
PointerFocusTarget::WindowUI(e) => e.alive(),
|
PointerFocusTarget::WindowUI(e) => e.alive(),
|
||||||
PointerFocusTarget::ResizeFork(f) => f.alive(),
|
PointerFocusTarget::ResizeFork(f) => f.alive(),
|
||||||
|
PointerFocusTarget::ZoomUI(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -233,6 +236,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::enter(u, seat, data, event),
|
PointerFocusTarget::StackUI(u) => PointerTarget::enter(u, seat, data, event),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::enter(u, seat, data, event),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::enter(u, seat, data, event),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::enter(f, seat, data, event),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::enter(f, seat, data, event),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::enter(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
||||||
|
|
@ -262,6 +266,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::motion(u, seat, data, event),
|
PointerFocusTarget::StackUI(u) => PointerTarget::motion(u, seat, data, event),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::motion(u, seat, data, event),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::motion(u, seat, data, event),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::motion(f, seat, data, event),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::motion(f, seat, data, event),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::motion(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
|
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
|
||||||
|
|
@ -274,6 +279,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::relative_motion(f, seat, data, event)
|
PointerTarget::relative_motion(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::relative_motion(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||||
|
|
@ -284,6 +290,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::button(u, seat, data, event),
|
PointerFocusTarget::StackUI(u) => PointerTarget::button(u, seat, data, event),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::button(u, seat, data, event),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::button(u, seat, data, event),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::button(f, seat, data, event),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::button(f, seat, data, event),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::button(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||||
|
|
@ -294,6 +301,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::axis(u, seat, data, frame),
|
PointerFocusTarget::StackUI(u) => PointerTarget::axis(u, seat, data, frame),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::axis(u, seat, data, frame),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::axis(u, seat, data, frame),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::axis(f, seat, data, frame),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::axis(f, seat, data, frame),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::axis(e, seat, data, frame),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn frame(&self, seat: &Seat<State>, data: &mut State) {
|
fn frame(&self, seat: &Seat<State>, data: &mut State) {
|
||||||
|
|
@ -304,6 +312,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::frame(u, seat, data),
|
PointerFocusTarget::StackUI(u) => PointerTarget::frame(u, seat, data),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::frame(u, seat, data),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::frame(u, seat, data),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::frame(f, seat, data),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::frame(f, seat, data),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::frame(e, seat, data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||||
|
|
@ -322,6 +331,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::StackUI(u) => PointerTarget::leave(u, seat, data, serial, time),
|
PointerFocusTarget::StackUI(u) => PointerTarget::leave(u, seat, data, serial, time),
|
||||||
PointerFocusTarget::WindowUI(u) => PointerTarget::leave(u, seat, data, serial, time),
|
PointerFocusTarget::WindowUI(u) => PointerTarget::leave(u, seat, data, serial, time),
|
||||||
PointerFocusTarget::ResizeFork(f) => PointerTarget::leave(f, seat, data, serial, time),
|
PointerFocusTarget::ResizeFork(f) => PointerTarget::leave(f, seat, data, serial, time),
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::leave(e, seat, data, serial, time),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_swipe_begin(
|
fn gesture_swipe_begin(
|
||||||
|
|
@ -343,6 +353,9 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_swipe_begin(f, seat, data, event)
|
PointerTarget::gesture_swipe_begin(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => {
|
||||||
|
PointerTarget::gesture_swipe_begin(e, seat, data, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_swipe_update(
|
fn gesture_swipe_update(
|
||||||
|
|
@ -364,6 +377,9 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_swipe_update(f, seat, data, event)
|
PointerTarget::gesture_swipe_update(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => {
|
||||||
|
PointerTarget::gesture_swipe_update(e, seat, data, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_swipe_end(
|
fn gesture_swipe_end(
|
||||||
|
|
@ -385,6 +401,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_swipe_end(f, seat, data, event)
|
PointerTarget::gesture_swipe_end(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::gesture_swipe_end(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_pinch_begin(
|
fn gesture_pinch_begin(
|
||||||
|
|
@ -406,6 +423,9 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_pinch_begin(f, seat, data, event)
|
PointerTarget::gesture_pinch_begin(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => {
|
||||||
|
PointerTarget::gesture_pinch_begin(e, seat, data, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_pinch_update(
|
fn gesture_pinch_update(
|
||||||
|
|
@ -427,6 +447,9 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_pinch_update(f, seat, data, event)
|
PointerTarget::gesture_pinch_update(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => {
|
||||||
|
PointerTarget::gesture_pinch_update(e, seat, data, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_pinch_end(
|
fn gesture_pinch_end(
|
||||||
|
|
@ -448,6 +471,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_pinch_end(f, seat, data, event)
|
PointerTarget::gesture_pinch_end(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::gesture_pinch_end(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_hold_begin(
|
fn gesture_hold_begin(
|
||||||
|
|
@ -469,6 +493,9 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_hold_begin(f, seat, data, event)
|
PointerTarget::gesture_hold_begin(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => {
|
||||||
|
PointerTarget::gesture_hold_begin(e, seat, data, event)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn gesture_hold_end(&self, seat: &Seat<State>, data: &mut State, event: &GestureHoldEndEvent) {
|
fn gesture_hold_end(&self, seat: &Seat<State>, data: &mut State, event: &GestureHoldEndEvent) {
|
||||||
|
|
@ -483,6 +510,7 @@ impl PointerTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(f) => {
|
PointerFocusTarget::ResizeFork(f) => {
|
||||||
PointerTarget::gesture_hold_end(f, seat, data, event)
|
PointerTarget::gesture_hold_end(f, seat, data, event)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(e) => PointerTarget::gesture_hold_end(e, seat, data, event),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -498,6 +526,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
}
|
}
|
||||||
PointerFocusTarget::StackUI(stack) => TouchTarget::down(stack, seat, data, event, seq),
|
PointerFocusTarget::StackUI(stack) => TouchTarget::down(stack, seat, data, event, seq),
|
||||||
PointerFocusTarget::ResizeFork(fork) => TouchTarget::down(fork, seat, data, event, seq),
|
PointerFocusTarget::ResizeFork(fork) => TouchTarget::down(fork, seat, data, event, seq),
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::down(elem, seat, data, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,6 +538,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::WindowUI(window) => TouchTarget::up(window, seat, data, event, seq),
|
PointerFocusTarget::WindowUI(window) => TouchTarget::up(window, seat, data, event, seq),
|
||||||
PointerFocusTarget::StackUI(stack) => TouchTarget::up(stack, seat, data, event, seq),
|
PointerFocusTarget::StackUI(stack) => TouchTarget::up(stack, seat, data, event, seq),
|
||||||
PointerFocusTarget::ResizeFork(fork) => TouchTarget::up(fork, seat, data, event, seq),
|
PointerFocusTarget::ResizeFork(fork) => TouchTarget::up(fork, seat, data, event, seq),
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::up(elem, seat, data, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -526,6 +556,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(fork) => {
|
PointerFocusTarget::ResizeFork(fork) => {
|
||||||
TouchTarget::motion(fork, seat, data, event, seq)
|
TouchTarget::motion(fork, seat, data, event, seq)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::motion(elem, seat, data, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -537,6 +568,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::WindowUI(window) => TouchTarget::frame(window, seat, data, seq),
|
PointerFocusTarget::WindowUI(window) => TouchTarget::frame(window, seat, data, seq),
|
||||||
PointerFocusTarget::StackUI(stack) => TouchTarget::frame(stack, seat, data, seq),
|
PointerFocusTarget::StackUI(stack) => TouchTarget::frame(stack, seat, data, seq),
|
||||||
PointerFocusTarget::ResizeFork(fork) => TouchTarget::frame(fork, seat, data, seq),
|
PointerFocusTarget::ResizeFork(fork) => TouchTarget::frame(fork, seat, data, seq),
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -548,6 +580,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::WindowUI(window) => TouchTarget::cancel(window, seat, data, seq),
|
PointerFocusTarget::WindowUI(window) => TouchTarget::cancel(window, seat, data, seq),
|
||||||
PointerFocusTarget::StackUI(stack) => TouchTarget::cancel(stack, seat, data, seq),
|
PointerFocusTarget::StackUI(stack) => TouchTarget::cancel(stack, seat, data, seq),
|
||||||
PointerFocusTarget::ResizeFork(fork) => TouchTarget::cancel(fork, seat, data, seq),
|
PointerFocusTarget::ResizeFork(fork) => TouchTarget::cancel(fork, seat, data, seq),
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -563,6 +596,7 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(fork) => {
|
PointerFocusTarget::ResizeFork(fork) => {
|
||||||
TouchTarget::shape(fork, seat, data, event, seq)
|
TouchTarget::shape(fork, seat, data, event, seq)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => TouchTarget::shape(elem, seat, data, event, seq),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -586,6 +620,9 @@ impl TouchTarget<State> for PointerFocusTarget {
|
||||||
PointerFocusTarget::ResizeFork(fork) => {
|
PointerFocusTarget::ResizeFork(fork) => {
|
||||||
TouchTarget::orientation(fork, seat, data, event, seq)
|
TouchTarget::orientation(fork, seat, data, event, seq)
|
||||||
}
|
}
|
||||||
|
PointerFocusTarget::ZoomUI(elem) => {
|
||||||
|
TouchTarget::orientation(elem, seat, data, event, seq)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -734,7 +771,8 @@ impl WaylandFocus for PointerFocusTarget {
|
||||||
PointerFocusTarget::WlSurface { surface, .. } => Cow::Borrowed(surface),
|
PointerFocusTarget::WlSurface { surface, .. } => Cow::Borrowed(surface),
|
||||||
PointerFocusTarget::ResizeFork(_)
|
PointerFocusTarget::ResizeFork(_)
|
||||||
| PointerFocusTarget::StackUI(_)
|
| PointerFocusTarget::StackUI(_)
|
||||||
| PointerFocusTarget::WindowUI(_) => {
|
| PointerFocusTarget::WindowUI(_)
|
||||||
|
| PointerFocusTarget::ZoomUI(_) => {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -751,7 +789,7 @@ impl WaylandFocus for PointerFocusTarget {
|
||||||
.wl_surface()
|
.wl_surface()
|
||||||
.map(|s| s.id().same_client_as(object_id))
|
.map(|s| s.id().same_client_as(object_id))
|
||||||
.unwrap_or(false),
|
.unwrap_or(false),
|
||||||
PointerFocusTarget::ResizeFork(_) => false,
|
PointerFocusTarget::ResizeFork(_) | PointerFocusTarget::ZoomUI(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
300
src/shell/mod.rs
300
src/shell/mod.rs
|
|
@ -10,10 +10,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use wayland_backend::server::ClientId;
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
use crate::{
|
use crate::wayland::{handlers::data_device, protocols::workspace::WorkspaceCapabilities};
|
||||||
utils::{float::NextDown, tween::EasePoint},
|
|
||||||
wayland::{handlers::data_device, protocols::workspace::WorkspaceCapabilities},
|
|
||||||
};
|
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
workspace::{WorkspaceLayout, WorkspaceMode},
|
workspace::{WorkspaceLayout, WorkspaceMode},
|
||||||
TileBehavior, ZoomConfig, ZoomMovement,
|
TileBehavior, ZoomConfig, ZoomMovement,
|
||||||
|
|
@ -85,9 +82,11 @@ pub mod grabs;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
mod seats;
|
mod seats;
|
||||||
mod workspace;
|
mod workspace;
|
||||||
|
pub mod zoom;
|
||||||
pub use self::element::{CosmicMapped, CosmicMappedRenderElement, CosmicSurface};
|
pub use self::element::{CosmicMapped, CosmicMappedRenderElement, CosmicSurface};
|
||||||
pub use self::seats::*;
|
pub use self::seats::*;
|
||||||
pub use self::workspace::*;
|
pub use self::workspace::*;
|
||||||
|
use self::zoom::{OutputZoomState, ZoomState};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
element::{
|
element::{
|
||||||
|
|
@ -248,116 +247,6 @@ pub struct PendingLayer {
|
||||||
pub output: Output,
|
pub output: Output,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ZoomState {
|
|
||||||
seat: Seat<State>,
|
|
||||||
level: f64,
|
|
||||||
movement: ZoomMovement,
|
|
||||||
previous_level: Option<(f64, Instant)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct OutputZoomState {
|
|
||||||
focal_point: Point<f64, Local>,
|
|
||||||
previous_point: Option<(Point<f64, Local>, Instant)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputZoomState {
|
|
||||||
pub fn new(
|
|
||||||
seat: &Seat<State>,
|
|
||||||
output: &Output,
|
|
||||||
movement: ZoomMovement,
|
|
||||||
level: f64,
|
|
||||||
) -> OutputZoomState {
|
|
||||||
let cursor_position = seat.get_pointer().unwrap().current_location().as_global();
|
|
||||||
let output_geometry = output.geometry().to_f64();
|
|
||||||
let focal_point = if output_geometry.contains(cursor_position) {
|
|
||||||
match movement {
|
|
||||||
ZoomMovement::Continuously | ZoomMovement::OnEdge => {
|
|
||||||
cursor_position.to_local(&output)
|
|
||||||
}
|
|
||||||
ZoomMovement::Centered => {
|
|
||||||
let mut zoomed_output_geometry = output_geometry;
|
|
||||||
zoomed_output_geometry = zoomed_output_geometry.downscale(level);
|
|
||||||
zoomed_output_geometry.loc =
|
|
||||||
cursor_position - zoomed_output_geometry.size.downscale(2.).to_point();
|
|
||||||
|
|
||||||
let mut focal_point = zoomed_output_geometry
|
|
||||||
.loc
|
|
||||||
.to_local(&output)
|
|
||||||
.upscale(level)
|
|
||||||
.to_global(&output);
|
|
||||||
focal_point.x = focal_point.x.clamp(
|
|
||||||
output_geometry.loc.x as f64,
|
|
||||||
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
focal_point.y = focal_point.y.clamp(
|
|
||||||
output_geometry.loc.y as f64,
|
|
||||||
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
focal_point.to_local(&output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(output_geometry.size.w / 2., output_geometry.size.h / 2.).into()
|
|
||||||
};
|
|
||||||
|
|
||||||
OutputZoomState {
|
|
||||||
focal_point,
|
|
||||||
previous_point: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focal_point(&mut self) -> Point<f64, Local> {
|
|
||||||
if let Some((old_point, start)) = self.previous_point.as_ref() {
|
|
||||||
let duration_since = Instant::now().duration_since(*start);
|
|
||||||
if duration_since > ANIMATION_DURATION {
|
|
||||||
self.previous_point.take();
|
|
||||||
return self.focal_point;
|
|
||||||
}
|
|
||||||
|
|
||||||
let percentage =
|
|
||||||
duration_since.as_millis() as f32 / ANIMATION_DURATION.as_millis() as f32;
|
|
||||||
ease(
|
|
||||||
EaseInOutCubic,
|
|
||||||
EasePoint(*old_point),
|
|
||||||
EasePoint(self.focal_point),
|
|
||||||
percentage,
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
} else {
|
|
||||||
self.focal_point
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZoomState {
|
|
||||||
pub fn level(&self, output: Option<&Output>) -> (Seat<State>, Point<f64, Global>, f64) {
|
|
||||||
let active_output = self.seat.active_output();
|
|
||||||
let output = output.unwrap_or(&active_output);
|
|
||||||
let output_state = output.user_data().get_or_insert_threadsafe(|| {
|
|
||||||
Mutex::new(OutputZoomState::new(
|
|
||||||
&self.seat,
|
|
||||||
output,
|
|
||||||
self.movement,
|
|
||||||
self.level,
|
|
||||||
))
|
|
||||||
});
|
|
||||||
let focal_point = output_state.lock().unwrap().focal_point().to_global(output);
|
|
||||||
|
|
||||||
if let Some((old_level, start)) = self.previous_level.as_ref() {
|
|
||||||
let percentage = Instant::now().duration_since(*start).as_millis() as f32
|
|
||||||
/ ANIMATION_DURATION.as_millis() as f32;
|
|
||||||
(
|
|
||||||
self.seat.clone(),
|
|
||||||
focal_point,
|
|
||||||
ease(EaseInOutCubic, *old_level, self.level, percentage),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(self.seat.clone(), focal_point, self.level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Shell {
|
pub struct Shell {
|
||||||
pub workspaces: Workspaces,
|
pub workspaces: Workspaces,
|
||||||
|
|
@ -1279,6 +1168,20 @@ impl Common {
|
||||||
&self.xdg_activation_state,
|
&self.xdg_activation_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(state) = shell.zoom_state.as_ref() {
|
||||||
|
output.user_data().insert_if_missing_threadsafe(|| {
|
||||||
|
Mutex::new(OutputZoomState::new(
|
||||||
|
&state.seat,
|
||||||
|
output,
|
||||||
|
state.level,
|
||||||
|
state.increment,
|
||||||
|
state.movement,
|
||||||
|
self.event_loop_handle.clone(),
|
||||||
|
shell.theme.clone(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
self.refresh(); // fixes indicies of any moved workspaces
|
self.refresh(); // fixes indicies of any moved workspaces
|
||||||
}
|
}
|
||||||
|
|
@ -1941,18 +1844,8 @@ impl Shell {
|
||||||
state.previous_level.is_some()
|
state.previous_level.is_some()
|
||||||
|| self.outputs().any(|o| {
|
|| self.outputs().any(|o| {
|
||||||
o.user_data()
|
o.user_data()
|
||||||
.get_or_insert_threadsafe(|| {
|
.get::<Mutex<OutputZoomState>>()
|
||||||
Mutex::new(OutputZoomState::new(
|
.is_some_and(|state| state.lock().unwrap().is_animating())
|
||||||
&state.seat,
|
|
||||||
o,
|
|
||||||
state.movement,
|
|
||||||
state.level,
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.previous_point
|
|
||||||
.is_some()
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -2092,8 +1985,9 @@ impl Shell {
|
||||||
&mut self,
|
&mut self,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
level: f64,
|
level: f64,
|
||||||
movement: ZoomMovement,
|
zoom_config: &ZoomConfig,
|
||||||
animate: bool,
|
animate: bool,
|
||||||
|
loop_handle: &LoopHandle<'static, State>,
|
||||||
) {
|
) {
|
||||||
if self.zoom_state.is_none() && level == 1. {
|
if self.zoom_state.is_none() && level == 1. {
|
||||||
return;
|
return;
|
||||||
|
|
@ -2104,13 +1998,38 @@ impl Shell {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
old_state.level
|
for output in self.outputs() {
|
||||||
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
output_state.lock().unwrap().update(
|
||||||
|
level,
|
||||||
|
zoom_config.view_moves,
|
||||||
|
zoom_config.increment,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
old_state.animating_level()
|
||||||
} else {
|
} else {
|
||||||
for output in self.outputs() {
|
for output in self.outputs() {
|
||||||
if let Some(output_state) = output.user_data().get::<Mutex<OutputZoomState>>() {
|
let output_state = output.user_data().get_or_insert_threadsafe(|| {
|
||||||
*output_state.lock().unwrap() =
|
Mutex::new(OutputZoomState::new(
|
||||||
OutputZoomState::new(seat, output, movement, level);
|
seat,
|
||||||
}
|
output,
|
||||||
|
level,
|
||||||
|
zoom_config.increment,
|
||||||
|
zoom_config.view_moves,
|
||||||
|
loop_handle.clone(),
|
||||||
|
self.theme.clone(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
*output_state.lock().unwrap() = OutputZoomState::new(
|
||||||
|
seat,
|
||||||
|
output,
|
||||||
|
level,
|
||||||
|
zoom_config.increment,
|
||||||
|
zoom_config.view_moves,
|
||||||
|
loop_handle.clone(),
|
||||||
|
self.theme.clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
1.
|
1.
|
||||||
};
|
};
|
||||||
|
|
@ -2118,7 +2037,8 @@ impl Shell {
|
||||||
self.zoom_state = Some(ZoomState {
|
self.zoom_state = Some(ZoomState {
|
||||||
seat: seat.clone(),
|
seat: seat.clone(),
|
||||||
level,
|
level,
|
||||||
movement,
|
increment: zoom_config.increment,
|
||||||
|
movement: zoom_config.view_moves,
|
||||||
previous_level: animate.then_some((previous_level, Instant::now())),
|
previous_level: animate.then_some((previous_level, Instant::now())),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -2134,103 +2054,19 @@ impl Shell {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = seat.active_output();
|
let cursor_position = seat.get_pointer().unwrap().current_location().as_global();
|
||||||
let output_state = output.user_data().get_or_insert_threadsafe(|| {
|
|
||||||
Mutex::new(OutputZoomState::new(seat, &output, movement, state.level))
|
|
||||||
});
|
|
||||||
let mut output_state_ref = output_state.lock().unwrap();
|
|
||||||
|
|
||||||
// animate movement type changes
|
state.update_focal_point(
|
||||||
if state.movement != movement {
|
&seat.active_output(),
|
||||||
output_state_ref.previous_point =
|
cursor_position,
|
||||||
Some((output_state_ref.focal_point, Instant::now()));
|
original_position,
|
||||||
state.movement = movement;
|
movement,
|
||||||
}
|
);
|
||||||
|
|
||||||
let cursor_position = seat
|
|
||||||
.get_pointer()
|
|
||||||
.unwrap()
|
|
||||||
.current_location()
|
|
||||||
.as_global()
|
|
||||||
.to_local(&output);
|
|
||||||
match movement {
|
|
||||||
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
|
|
||||||
ZoomMovement::OnEdge => {
|
|
||||||
let output_geometry = output.geometry().to_f64();
|
|
||||||
let mut zoomed_output_geometry = output_geometry;
|
|
||||||
|
|
||||||
zoomed_output_geometry.loc -= output_state_ref.focal_point.to_global(&output);
|
|
||||||
zoomed_output_geometry = zoomed_output_geometry.downscale(state.level);
|
|
||||||
zoomed_output_geometry.loc += output_state_ref.focal_point.to_global(&output);
|
|
||||||
|
|
||||||
if !zoomed_output_geometry.contains(original_position) {
|
|
||||||
zoomed_output_geometry.loc = cursor_position.to_global(&output)
|
|
||||||
- zoomed_output_geometry.size.downscale(2.).to_point();
|
|
||||||
let mut focal_point = zoomed_output_geometry
|
|
||||||
.loc
|
|
||||||
.to_local(&output)
|
|
||||||
.upscale(state.level)
|
|
||||||
.to_global(&output);
|
|
||||||
focal_point.x = focal_point.x.clamp(
|
|
||||||
output_geometry.loc.x as f64,
|
|
||||||
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
focal_point.y = focal_point.y.clamp(
|
|
||||||
output_geometry.loc.y as f64,
|
|
||||||
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
output_state_ref.previous_point =
|
|
||||||
Some((output_state_ref.focal_point, Instant::now()));
|
|
||||||
output_state_ref.focal_point = focal_point.to_local(&output);
|
|
||||||
} else if !zoomed_output_geometry.contains(cursor_position.to_global(&output)) {
|
|
||||||
let mut diff = output_state_ref.focal_point.to_global(&output)
|
|
||||||
+ (cursor_position.to_global(&output) - original_position)
|
|
||||||
.upscale(state.level);
|
|
||||||
diff.x = diff.x.clamp(
|
|
||||||
output_geometry.loc.x as f64,
|
|
||||||
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
diff.y = diff.y.clamp(
|
|
||||||
output_geometry.loc.y as f64,
|
|
||||||
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
diff -= output_state_ref.focal_point.to_global(&output);
|
|
||||||
|
|
||||||
output_state_ref.focal_point += diff.as_logical().as_local();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ZoomMovement::Centered => {
|
|
||||||
let output_geometry = output.geometry().to_f64();
|
|
||||||
|
|
||||||
let mut zoomed_output_geometry = output_geometry;
|
|
||||||
zoomed_output_geometry = zoomed_output_geometry.downscale(state.level);
|
|
||||||
zoomed_output_geometry.loc = cursor_position.to_global(&output)
|
|
||||||
- zoomed_output_geometry.size.downscale(2.).to_point();
|
|
||||||
|
|
||||||
let mut focal_point = zoomed_output_geometry
|
|
||||||
.loc
|
|
||||||
.to_local(&output)
|
|
||||||
.upscale(state.level)
|
|
||||||
.to_global(&output);
|
|
||||||
focal_point.x = focal_point.x.clamp(
|
|
||||||
output_geometry.loc.x as f64,
|
|
||||||
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
focal_point.y = focal_point.y.clamp(
|
|
||||||
output_geometry.loc.y as f64,
|
|
||||||
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
|
||||||
);
|
|
||||||
output_state_ref.focal_point = focal_point.to_local(&output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zoom_level(
|
pub fn zoom_state(&self) -> Option<&ZoomState> {
|
||||||
&self,
|
self.zoom_state.as_ref()
|
||||||
output: Option<&Output>,
|
|
||||||
) -> Option<(Seat<State>, Point<f64, Global>, f64)> {
|
|
||||||
self.zoom_state.as_ref().map(|s| s.level(output))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(
|
fn refresh(
|
||||||
|
|
@ -2282,6 +2118,18 @@ impl Shell {
|
||||||
if zoom_state.level == 1. && zoom_state.previous_level.is_none() {
|
if zoom_state.level == 1. && zoom_state.previous_level.is_none() {
|
||||||
self.zoom_state.take();
|
self.zoom_state.take();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.zoom_state.is_some() {
|
||||||
|
for output in self.outputs() {
|
||||||
|
output
|
||||||
|
.user_data()
|
||||||
|
.get::<Mutex<OutputZoomState>>()
|
||||||
|
.unwrap()
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.workspaces
|
self.workspaces
|
||||||
|
|
|
||||||
999
src/shell/zoom.rs
Normal file
999
src/shell/zoom.rs
Normal file
|
|
@ -0,0 +1,999 @@
|
||||||
|
use std::{sync::Mutex, time::Instant};
|
||||||
|
|
||||||
|
use calloop::LoopHandle;
|
||||||
|
use cosmic::{
|
||||||
|
iced::{alignment::Vertical, Alignment, Background, Border, Length},
|
||||||
|
iced_widget, theme,
|
||||||
|
widget::{self, icon::Named},
|
||||||
|
Apply,
|
||||||
|
};
|
||||||
|
use cosmic_comp_config::ZoomMovement;
|
||||||
|
use keyframe::{ease, functions::EaseInOutCubic};
|
||||||
|
use smithay::{
|
||||||
|
backend::renderer::{element::AsRenderElements, ImportMem, Renderer},
|
||||||
|
desktop::space::SpaceElement,
|
||||||
|
input::{
|
||||||
|
pointer::{
|
||||||
|
AxisFrame, ButtonEvent, Focus, GestureHoldBeginEvent, GestureHoldEndEvent,
|
||||||
|
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent,
|
||||||
|
GestureSwipeBeginEvent, GestureSwipeEndEvent, GestureSwipeUpdateEvent,
|
||||||
|
MotionEvent as PointerMotionEvent, PointerTarget, RelativeMotionEvent,
|
||||||
|
},
|
||||||
|
touch::{
|
||||||
|
DownEvent, MotionEvent as TouchMotionEvent, OrientationEvent, ShapeEvent, TouchTarget,
|
||||||
|
UpEvent,
|
||||||
|
},
|
||||||
|
Seat,
|
||||||
|
},
|
||||||
|
output::Output,
|
||||||
|
utils::{IsAlive, Point, Rectangle, Serial, Size},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
state::State,
|
||||||
|
utils::{
|
||||||
|
float::NextDown,
|
||||||
|
iced::{IcedElement, Program},
|
||||||
|
prelude::*,
|
||||||
|
tween::EasePoint,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
check_grab_preconditions,
|
||||||
|
focus::target::PointerFocusTarget,
|
||||||
|
grabs::{ContextMenu, Item, MenuAlignment, MenuGrab},
|
||||||
|
ANIMATION_DURATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ZoomState {
|
||||||
|
pub(super) seat: Seat<State>,
|
||||||
|
pub(super) level: f64,
|
||||||
|
pub(super) increment: u32,
|
||||||
|
pub(super) movement: ZoomMovement,
|
||||||
|
pub(super) previous_level: Option<(f64, Instant)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct OutputZoomState {
|
||||||
|
focal_point: Point<f64, Local>,
|
||||||
|
previous_point: Option<(Point<f64, Local>, Instant)>,
|
||||||
|
element: ZoomElement,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputZoomState {
|
||||||
|
pub fn new(
|
||||||
|
seat: &Seat<State>,
|
||||||
|
output: &Output,
|
||||||
|
level: f64,
|
||||||
|
increment: u32,
|
||||||
|
movement: ZoomMovement,
|
||||||
|
loop_handle: LoopHandle<'static, State>,
|
||||||
|
theme: cosmic::Theme,
|
||||||
|
) -> OutputZoomState {
|
||||||
|
let cursor_position = seat.get_pointer().unwrap().current_location().as_global();
|
||||||
|
let output_geometry = output.geometry().to_f64();
|
||||||
|
let focal_point = if output_geometry.contains(cursor_position) {
|
||||||
|
match movement {
|
||||||
|
ZoomMovement::Continuously | ZoomMovement::OnEdge => {
|
||||||
|
cursor_position.to_local(&output)
|
||||||
|
}
|
||||||
|
ZoomMovement::Centered => {
|
||||||
|
let mut zoomed_output_geometry =
|
||||||
|
output.zoomed_geometry(level).unwrap().to_f64();
|
||||||
|
zoomed_output_geometry.loc =
|
||||||
|
cursor_position - zoomed_output_geometry.size.downscale(2.).to_point();
|
||||||
|
|
||||||
|
let mut focal_point = zoomed_output_geometry
|
||||||
|
.loc
|
||||||
|
.to_local(&output)
|
||||||
|
.upscale(level)
|
||||||
|
.to_global(&output);
|
||||||
|
focal_point.x = focal_point.x.clamp(
|
||||||
|
output_geometry.loc.x as f64,
|
||||||
|
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
focal_point.y = focal_point.y.clamp(
|
||||||
|
output_geometry.loc.y as f64,
|
||||||
|
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
focal_point.to_local(&output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(output_geometry.size.w / 2., output_geometry.size.h / 2.).into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let program = ZoomProgram::new(level, movement, increment);
|
||||||
|
let element = IcedElement::new(program, Size::default(), loop_handle, theme);
|
||||||
|
let mut size = element.minimum_size();
|
||||||
|
size.w = (size.w + 32/*TODO: figure out why iced is calculating too little*/)
|
||||||
|
.min(output_geometry.size.w.round() as i32);
|
||||||
|
element.set_activate(true);
|
||||||
|
element.resize(size);
|
||||||
|
element.output_enter(output, Rectangle::new(Point::from((0, 0)), size));
|
||||||
|
element.refresh();
|
||||||
|
|
||||||
|
OutputZoomState {
|
||||||
|
focal_point,
|
||||||
|
previous_point: None,
|
||||||
|
element,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focal_point(&mut self) -> Point<f64, Local> {
|
||||||
|
if let Some((old_point, start)) = self.previous_point.as_ref() {
|
||||||
|
let duration_since = Instant::now().duration_since(*start);
|
||||||
|
if duration_since > ANIMATION_DURATION {
|
||||||
|
self.previous_point.take();
|
||||||
|
return self.focal_point;
|
||||||
|
}
|
||||||
|
|
||||||
|
let percentage =
|
||||||
|
duration_since.as_millis() as f32 / ANIMATION_DURATION.as_millis() as f32;
|
||||||
|
ease(
|
||||||
|
EaseInOutCubic,
|
||||||
|
EasePoint(*old_point),
|
||||||
|
EasePoint(self.focal_point),
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
} else {
|
||||||
|
self.focal_point
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_animating(&self) -> bool {
|
||||||
|
self.previous_point.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn refresh(&self) {
|
||||||
|
self.element.refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self, level: f64, movement: ZoomMovement, increment: u32) {
|
||||||
|
self.element.queue_message(ZoomMessage::Update {
|
||||||
|
level,
|
||||||
|
movement,
|
||||||
|
increment,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<R, C>(&mut self, renderer: &mut R, output: &Output) -> Vec<C>
|
||||||
|
where
|
||||||
|
C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
|
||||||
|
R: Renderer + ImportMem,
|
||||||
|
<R as Renderer>::TextureId: Send + Clone + 'static,
|
||||||
|
{
|
||||||
|
let size = self.element.current_size().to_f64();
|
||||||
|
let output_geo = output.geometry().to_f64();
|
||||||
|
let scale = output.current_scale();
|
||||||
|
let location = Point::from((
|
||||||
|
output_geo.size.w / 2. - size.w / 2.,
|
||||||
|
output_geo.size.h / 4. * 3. - size.h / 2.,
|
||||||
|
))
|
||||||
|
.to_physical(scale.fractional_scale())
|
||||||
|
.to_i32_round();
|
||||||
|
|
||||||
|
self.element
|
||||||
|
.render_elements(renderer, location, scale.fractional_scale().into(), 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZoomState {
|
||||||
|
pub fn current_level(&self) -> f64 {
|
||||||
|
self.level
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn animating_level(&self) -> f64 {
|
||||||
|
if let Some((old_level, start)) = self.previous_level.as_ref() {
|
||||||
|
let percentage = Instant::now().duration_since(*start).as_millis() as f32
|
||||||
|
/ ANIMATION_DURATION.as_millis() as f32;
|
||||||
|
|
||||||
|
ease(EaseInOutCubic, *old_level, self.level, percentage)
|
||||||
|
} else {
|
||||||
|
self.level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_seat(&self) -> Seat<State> {
|
||||||
|
self.seat.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focal_point(&self, output: Option<&Output>) -> Point<f64, Global> {
|
||||||
|
let active_output = self.seat.active_output();
|
||||||
|
let output = output.unwrap_or(&active_output);
|
||||||
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
let res = output_state.lock().unwrap().focal_point().to_global(output);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_focal_point(
|
||||||
|
&mut self,
|
||||||
|
output: &Output,
|
||||||
|
cursor_position: Point<f64, Global>,
|
||||||
|
original_position: Point<f64, Global>,
|
||||||
|
movement: ZoomMovement,
|
||||||
|
) {
|
||||||
|
let output_geometry = output.geometry().to_f64();
|
||||||
|
let mut zoomed_output_geometry = output.zoomed_geometry(self.level).unwrap().to_f64();
|
||||||
|
|
||||||
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
let mut output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
|
// animate movement type changes
|
||||||
|
if self.movement != movement {
|
||||||
|
output_state_ref.previous_point = Some((output_state_ref.focal_point, Instant::now()));
|
||||||
|
self.movement = movement;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursor_position = cursor_position.to_local(output);
|
||||||
|
match movement {
|
||||||
|
ZoomMovement::Continuously => output_state_ref.focal_point = cursor_position,
|
||||||
|
ZoomMovement::OnEdge => {
|
||||||
|
if !zoomed_output_geometry.contains(original_position) {
|
||||||
|
zoomed_output_geometry.loc = cursor_position.to_global(&output)
|
||||||
|
- zoomed_output_geometry.size.downscale(2.).to_point();
|
||||||
|
let mut focal_point = zoomed_output_geometry
|
||||||
|
.loc
|
||||||
|
.to_local(&output)
|
||||||
|
.upscale(
|
||||||
|
output_geometry.size.w
|
||||||
|
/ (output_geometry.size.w - zoomed_output_geometry.size.w),
|
||||||
|
)
|
||||||
|
.to_global(&output);
|
||||||
|
focal_point.x = focal_point.x.clamp(
|
||||||
|
output_geometry.loc.x as f64,
|
||||||
|
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
focal_point.y = focal_point.y.clamp(
|
||||||
|
output_geometry.loc.y as f64,
|
||||||
|
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
output_state_ref.previous_point =
|
||||||
|
Some((output_state_ref.focal_point, Instant::now()));
|
||||||
|
output_state_ref.focal_point = focal_point.to_local(&output);
|
||||||
|
} else if !zoomed_output_geometry.contains(cursor_position.to_global(&output)) {
|
||||||
|
let mut diff = output_state_ref.focal_point.to_global(&output)
|
||||||
|
+ (cursor_position.to_global(&output) - original_position)
|
||||||
|
.upscale(self.level);
|
||||||
|
diff.x = diff.x.clamp(
|
||||||
|
output_geometry.loc.x as f64,
|
||||||
|
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
diff.y = diff.y.clamp(
|
||||||
|
output_geometry.loc.y as f64,
|
||||||
|
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
diff -= output_state_ref.focal_point.to_global(&output);
|
||||||
|
|
||||||
|
output_state_ref.focal_point += diff.as_logical().as_local();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZoomMovement::Centered => {
|
||||||
|
zoomed_output_geometry.loc = cursor_position.to_global(&output)
|
||||||
|
- zoomed_output_geometry.size.downscale(2.).to_point();
|
||||||
|
|
||||||
|
let mut focal_point = zoomed_output_geometry
|
||||||
|
.loc
|
||||||
|
.to_local(&output)
|
||||||
|
.upscale(
|
||||||
|
output_geometry.size.w
|
||||||
|
/ (output_geometry.size.w - zoomed_output_geometry.size.w),
|
||||||
|
)
|
||||||
|
.to_global(&output);
|
||||||
|
focal_point.x = focal_point.x.clamp(
|
||||||
|
output_geometry.loc.x as f64,
|
||||||
|
((output_geometry.loc.x + output_geometry.size.w) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
focal_point.y = focal_point.y.clamp(
|
||||||
|
output_geometry.loc.y as f64,
|
||||||
|
((output_geometry.loc.y + output_geometry.size.h) as f64).next_lower(), // FIXME: Replace with f64::next_down when stable
|
||||||
|
);
|
||||||
|
output_state_ref.focal_point = focal_point.to_local(&output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface_under(
|
||||||
|
&self,
|
||||||
|
output: &Output,
|
||||||
|
pos: Point<f64, Global>,
|
||||||
|
) -> Option<(PointerFocusTarget, Point<f64, Global>)> {
|
||||||
|
let output_geometry = output.geometry();
|
||||||
|
let zoomed_output_geometry = output.zoomed_geometry(self.level).unwrap().to_f64();
|
||||||
|
|
||||||
|
let local_pos = global_pos_to_screen_space(pos, output, self.level);
|
||||||
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
let output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
|
let size = output_state_ref.element.current_size().to_f64().as_local();
|
||||||
|
let location = Point::<f64, Local>::from((
|
||||||
|
output_geometry.size.w as f64 / 2. - size.w / 2.,
|
||||||
|
output_geometry.size.h as f64 / 4. * 3. - size.h / 2.,
|
||||||
|
));
|
||||||
|
let area = Rectangle::<_, Local>::new(location, size);
|
||||||
|
|
||||||
|
if area.contains(local_pos) {
|
||||||
|
return Some((
|
||||||
|
PointerFocusTarget::ZoomUI(output_state_ref.element.clone().into()),
|
||||||
|
{
|
||||||
|
// and vise-versa from screen-space to zoom-space...
|
||||||
|
let scaled_loc = location.downscale(self.level);
|
||||||
|
let global_loc = Point::<f64, Global>::from((scaled_loc.x, scaled_loc.y))
|
||||||
|
+ zoomed_output_geometry.loc;
|
||||||
|
|
||||||
|
// HACK: We do have the right position now `global_loc`, but smithay calculates
|
||||||
|
// the relative position for us... Which will be wrong given the cursor movement will
|
||||||
|
// be scaled, while this element isn't, as it exists in screen-space and not workspace-space.
|
||||||
|
// So we shift the location relatively to make up for the scaled movement...
|
||||||
|
let diff = (pos - global_loc).upscale(self.level - 1.);
|
||||||
|
|
||||||
|
global_loc - diff
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<R, C>(renderer: &mut R, output: &Output) -> Vec<C>
|
||||||
|
where
|
||||||
|
C: From<<IcedElement<ZoomProgram> as AsRenderElements<R>>::RenderElement>,
|
||||||
|
R: Renderer + ImportMem,
|
||||||
|
<R as Renderer>::TextureId: Send + Clone + 'static,
|
||||||
|
{
|
||||||
|
let output_state = output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
output_state.lock().unwrap().render(renderer, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_pos_to_screen_space(
|
||||||
|
pos: impl Into<Point<f64, Global>>,
|
||||||
|
output: &Output,
|
||||||
|
level: f64,
|
||||||
|
) -> Point<f64, Local> {
|
||||||
|
let pos = pos.into();
|
||||||
|
let zoomed_output_geometry = output.zoomed_geometry(level).unwrap().to_f64();
|
||||||
|
|
||||||
|
// lets try to get the global cursor position into screen space
|
||||||
|
let relative_to_zoom_geo = Point::<f64, Local>::from((
|
||||||
|
pos.x - zoomed_output_geometry.loc.x,
|
||||||
|
pos.y - zoomed_output_geometry.loc.y,
|
||||||
|
));
|
||||||
|
relative_to_zoom_geo.upscale(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ZoomElement = IcedElement<ZoomProgram>;
|
||||||
|
|
||||||
|
pub struct ZoomProgram {
|
||||||
|
level: f64,
|
||||||
|
increments: Vec<u32>,
|
||||||
|
increment_idx: usize,
|
||||||
|
movement: ZoomMovement,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum ZoomMessage {
|
||||||
|
Decrease,
|
||||||
|
Increase,
|
||||||
|
Increment,
|
||||||
|
More,
|
||||||
|
Close,
|
||||||
|
Update {
|
||||||
|
level: f64,
|
||||||
|
increment: u32,
|
||||||
|
movement: ZoomMovement,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum MenuMessage {
|
||||||
|
ViewContinuously,
|
||||||
|
ViewOnEdge,
|
||||||
|
ViewCentered,
|
||||||
|
OpenSettings,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZoomProgram {
|
||||||
|
pub fn new(level: f64, movement: ZoomMovement, increment: u32) -> Self {
|
||||||
|
let mut increments = vec![25, 50, 100, 150, 200];
|
||||||
|
if !increments.contains(&increment) {
|
||||||
|
increments.push(increment);
|
||||||
|
}
|
||||||
|
increments.sort();
|
||||||
|
let increment_idx = increments.iter().position(|val| *val == increment).unwrap();
|
||||||
|
|
||||||
|
ZoomProgram {
|
||||||
|
level,
|
||||||
|
increments,
|
||||||
|
increment_idx,
|
||||||
|
movement,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Program for ZoomProgram {
|
||||||
|
type Message = ZoomMessage;
|
||||||
|
|
||||||
|
fn view(&self) -> cosmic::Element<'_, Self::Message> {
|
||||||
|
widget::row::with_children(vec![
|
||||||
|
widget::button::icon(Named::new("list-remove-symbolic").size(16).prefer_svg(true))
|
||||||
|
.on_press(ZoomMessage::Decrease)
|
||||||
|
.into(),
|
||||||
|
widget::text(format!("{}%", (self.level * 100.).round()))
|
||||||
|
.align_y(Vertical::Center)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.into(),
|
||||||
|
widget::button::icon(Named::new("list-add-symbolic").size(16).prefer_svg(true))
|
||||||
|
.on_press(ZoomMessage::Increase)
|
||||||
|
.into(),
|
||||||
|
widget::divider::vertical::default().into(),
|
||||||
|
widget::button::text(format!("{}%", self.increments[self.increment_idx]))
|
||||||
|
.trailing_icon(Named::new("pan-down-symbolic").size(16).prefer_svg(true))
|
||||||
|
.on_press(ZoomMessage::Increment)
|
||||||
|
.class(theme::Button::MenuFolder)
|
||||||
|
.into(),
|
||||||
|
widget::button::icon(Named::new("view-more-symbolic").size(16).prefer_svg(true))
|
||||||
|
.on_press(ZoomMessage::More)
|
||||||
|
.into(),
|
||||||
|
widget::divider::vertical::default().into(),
|
||||||
|
widget::button::icon(
|
||||||
|
Named::new("window-close-symbolic")
|
||||||
|
.size(16)
|
||||||
|
.prefer_svg(true),
|
||||||
|
)
|
||||||
|
.on_press(ZoomMessage::Close)
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
|
.spacing(8.)
|
||||||
|
.height(Length::Fixed(32.))
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.apply(widget::container)
|
||||||
|
.padding(8)
|
||||||
|
.class(theme::Container::custom(|theme| {
|
||||||
|
let cosmic = theme.cosmic();
|
||||||
|
let component = &cosmic.background.component;
|
||||||
|
iced_widget::container::Style {
|
||||||
|
icon_color: Some(component.on.into()),
|
||||||
|
text_color: Some(component.on.into()),
|
||||||
|
background: Some(Background::Color(component.base.into())),
|
||||||
|
border: Border {
|
||||||
|
radius: cosmic.radius_s().into(),
|
||||||
|
width: 1.0,
|
||||||
|
color: component.divider.into(),
|
||||||
|
},
|
||||||
|
shadow: Default::default(),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
message: Self::Message,
|
||||||
|
loop_handle: &LoopHandle<'static, State>,
|
||||||
|
last_seat: Option<&(Seat<State>, Serial)>,
|
||||||
|
) -> cosmic::Task<Self::Message> {
|
||||||
|
match message {
|
||||||
|
ZoomMessage::Decrease => {
|
||||||
|
let _ = loop_handle.insert_idle(|state| {
|
||||||
|
let seat = state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.clone();
|
||||||
|
let increment =
|
||||||
|
state.common.config.cosmic_conf.accessibility_zoom.increment as f64 / 100.0;
|
||||||
|
|
||||||
|
state.update_zoom(&seat, -increment, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ZoomMessage::Increase => {
|
||||||
|
let _ = loop_handle.insert_idle(|state| {
|
||||||
|
let seat = state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.clone();
|
||||||
|
let increment =
|
||||||
|
state.common.config.cosmic_conf.accessibility_zoom.increment as f64 / 100.0;
|
||||||
|
|
||||||
|
state.update_zoom(&seat, increment, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ZoomMessage::More => {
|
||||||
|
let movement = self.movement;
|
||||||
|
if let Some((seat, serial)) = last_seat.cloned() {
|
||||||
|
let _ = loop_handle.insert_idle(move |state| {
|
||||||
|
if let Some(start_data) =
|
||||||
|
check_grab_preconditions(&seat, Some(serial), None)
|
||||||
|
{
|
||||||
|
let shell = state.common.shell.read().unwrap();
|
||||||
|
let output = seat.active_output();
|
||||||
|
|
||||||
|
if let Some(zoom_state) = shell.zoom_state() {
|
||||||
|
let location = global_pos_to_screen_space(
|
||||||
|
start_data.location().as_global(),
|
||||||
|
&output,
|
||||||
|
zoom_state.current_level(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output_geometry = output.geometry();
|
||||||
|
let output_state =
|
||||||
|
output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
let output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
|
let elem_size =
|
||||||
|
output_state_ref.element.current_size().to_f64().as_local();
|
||||||
|
let elem_location = Point::<f64, Local>::from((
|
||||||
|
output_geometry.size.w as f64 / 2. - elem_size.w / 2.,
|
||||||
|
output_geometry.size.h as f64 / 4. * 3. - elem_size.h / 2.,
|
||||||
|
));
|
||||||
|
let position = Point::<_, Local>::from((
|
||||||
|
location.x,
|
||||||
|
elem_location.y + elem_size.h,
|
||||||
|
));
|
||||||
|
|
||||||
|
let grab = MenuGrab::new(
|
||||||
|
start_data,
|
||||||
|
&seat,
|
||||||
|
vec![
|
||||||
|
Item::new("View moves with pointer", move |handle| {
|
||||||
|
let _ = handle.insert_idle(move |state| {
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.config
|
||||||
|
.cosmic_conf
|
||||||
|
.accessibility_zoom
|
||||||
|
.view_moves = ZoomMovement::Continuously;
|
||||||
|
state.common.update_config();
|
||||||
|
// TODO: Write config
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.toggled(movement == ZoomMovement::Continuously),
|
||||||
|
Item::new(
|
||||||
|
"View moves when pointer reaches edge",
|
||||||
|
move |handle| {
|
||||||
|
let _ = handle.insert_idle(move |state| {
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.config
|
||||||
|
.cosmic_conf
|
||||||
|
.accessibility_zoom
|
||||||
|
.view_moves = ZoomMovement::OnEdge;
|
||||||
|
state.common.update_config();
|
||||||
|
// TODO: Write config
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toggled(movement == ZoomMovement::OnEdge),
|
||||||
|
Item::new(
|
||||||
|
"View moves to keep pointer centered",
|
||||||
|
move |handle| {
|
||||||
|
let _ = handle.insert_idle(move |state| {
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.config
|
||||||
|
.cosmic_conf
|
||||||
|
.accessibility_zoom
|
||||||
|
.view_moves = ZoomMovement::Centered;
|
||||||
|
state.common.update_config();
|
||||||
|
// TODO: Write config
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.toggled(movement == ZoomMovement::Centered),
|
||||||
|
Item::new("Magnifier settings...", |_| { /* TODO */ }),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
position.to_global(&output).to_i32_round(),
|
||||||
|
MenuAlignment::HORIZONTALLY_CENTERED,
|
||||||
|
true,
|
||||||
|
state.common.event_loop_handle.clone(),
|
||||||
|
state.common.theme.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
std::mem::drop(shell);
|
||||||
|
if grab.is_touch_grab() {
|
||||||
|
seat.get_touch().unwrap().set_grab(state, grab, serial);
|
||||||
|
} else {
|
||||||
|
seat.get_pointer().unwrap().set_grab(
|
||||||
|
state,
|
||||||
|
grab,
|
||||||
|
serial,
|
||||||
|
Focus::Clear,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ZoomMessage::Increment => {
|
||||||
|
if let Some((seat, serial)) = last_seat.cloned() {
|
||||||
|
let increments = self.increments.clone();
|
||||||
|
let _ = loop_handle.insert_idle(move |state| {
|
||||||
|
if let Some(start_data) =
|
||||||
|
check_grab_preconditions(&seat, Some(serial), None)
|
||||||
|
{
|
||||||
|
let shell = state.common.shell.read().unwrap();
|
||||||
|
let output = seat.active_output();
|
||||||
|
|
||||||
|
if let Some(zoom_state) = shell.zoom_state() {
|
||||||
|
let location = global_pos_to_screen_space(
|
||||||
|
start_data.location().as_global(),
|
||||||
|
&output,
|
||||||
|
zoom_state.current_level(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let output_geometry = output.geometry();
|
||||||
|
let output_state =
|
||||||
|
output.user_data().get::<Mutex<OutputZoomState>>().unwrap();
|
||||||
|
let output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
|
let elem_size =
|
||||||
|
output_state_ref.element.current_size().to_f64().as_local();
|
||||||
|
let elem_location = Point::<f64, Local>::from((
|
||||||
|
output_geometry.size.w as f64 / 2. - elem_size.w / 2.,
|
||||||
|
output_geometry.size.h as f64 / 4. * 3. - elem_size.h / 2.,
|
||||||
|
));
|
||||||
|
let position = Point::<_, Local>::from((
|
||||||
|
location.x,
|
||||||
|
elem_location.y + elem_size.h,
|
||||||
|
));
|
||||||
|
|
||||||
|
let grab = MenuGrab::new(
|
||||||
|
start_data,
|
||||||
|
&seat,
|
||||||
|
increments.into_iter().map(|val| {
|
||||||
|
Item::new(format!("{}%", val), move |handle| {
|
||||||
|
let _ = handle.insert_idle(move |state| {
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.config
|
||||||
|
.cosmic_conf
|
||||||
|
.accessibility_zoom
|
||||||
|
.increment = val;
|
||||||
|
state.common.update_config();
|
||||||
|
// TODO: Write config
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
position.to_global(&output).to_i32_round(),
|
||||||
|
MenuAlignment::CENTERED,
|
||||||
|
true,
|
||||||
|
state.common.event_loop_handle.clone(),
|
||||||
|
state.common.theme.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
std::mem::drop(shell);
|
||||||
|
if grab.is_touch_grab() {
|
||||||
|
seat.get_touch().unwrap().set_grab(state, grab, serial);
|
||||||
|
} else {
|
||||||
|
seat.get_pointer().unwrap().set_grab(
|
||||||
|
state,
|
||||||
|
grab,
|
||||||
|
serial,
|
||||||
|
Focus::Clear,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
let new_increment = self.increments[idx];
|
||||||
|
let _ = loop_handle.insert_idle(move |state| {
|
||||||
|
state.common.config.cosmic_conf.accessibility_zoom.increment = new_increment;
|
||||||
|
// TODO: Write config
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
ZoomMessage::Close => {
|
||||||
|
let _ = loop_handle.insert_idle(|state| {
|
||||||
|
let seat = state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.last_active()
|
||||||
|
.clone();
|
||||||
|
state.common.shell.write().unwrap().trigger_zoom(
|
||||||
|
&seat,
|
||||||
|
1.0,
|
||||||
|
&state.common.config.cosmic_conf.accessibility_zoom,
|
||||||
|
true,
|
||||||
|
&state.common.event_loop_handle,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ZoomMessage::Update {
|
||||||
|
level,
|
||||||
|
increment,
|
||||||
|
movement,
|
||||||
|
} => {
|
||||||
|
self.level = level;
|
||||||
|
self.movement = movement;
|
||||||
|
|
||||||
|
if let Some(pos) = self.increments.iter().position(|val| *val == increment) {
|
||||||
|
self.increment_idx = pos;
|
||||||
|
} else {
|
||||||
|
let mut increments = vec![25, 50, 100, 150, 200];
|
||||||
|
if !increments.contains(&increment) {
|
||||||
|
increments.push(increment);
|
||||||
|
}
|
||||||
|
increments.sort();
|
||||||
|
self.increment_idx =
|
||||||
|
increments.iter().position(|val| *val == increment).unwrap();
|
||||||
|
self.increments = increments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cosmic::Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum ZoomFocusTarget {
|
||||||
|
Main(ZoomElement),
|
||||||
|
Menu(IcedElement<ContextMenu>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ZoomElement> for ZoomFocusTarget {
|
||||||
|
fn from(value: ZoomElement) -> Self {
|
||||||
|
ZoomFocusTarget::Main(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<IcedElement<ContextMenu>> for ZoomFocusTarget {
|
||||||
|
fn from(value: IcedElement<ContextMenu>) -> Self {
|
||||||
|
ZoomFocusTarget::Menu(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointerTarget<State> for ZoomFocusTarget {
|
||||||
|
fn enter(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::enter(elem, seat, data, event),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::enter(elem, seat, data, event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &PointerMotionEvent) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::motion(elem, seat, data, event),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::motion(elem, seat, data, event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn relative_motion(&self, seat: &Seat<State>, data: &mut State, event: &RelativeMotionEvent) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::relative_motion(elem, seat, data, event),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::relative_motion(elem, seat, data, event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button(&self, seat: &Seat<State>, data: &mut State, event: &ButtonEvent) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::button(elem, seat, data, event),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::button(elem, seat, data, event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn axis(&self, seat: &Seat<State>, data: &mut State, frame: AxisFrame) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::axis(elem, seat, data, frame),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::axis(elem, seat, data, frame),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&self, seat: &Seat<State>, data: &mut State) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::frame(elem, seat, data),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::frame(elem, seat, data),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_begin(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GestureSwipeBeginEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_update(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GestureSwipeUpdateEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_update(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_update(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_swipe_end(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GestureSwipeEndEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_end(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_swipe_end(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_begin(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GesturePinchBeginEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_update(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GesturePinchUpdateEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_update(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_update(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_pinch_end(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GesturePinchEndEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_end(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_pinch_end(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_hold_begin(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &GestureHoldBeginEvent,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => {
|
||||||
|
PointerTarget::gesture_hold_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
ZoomFocusTarget::Menu(elem) => {
|
||||||
|
PointerTarget::gesture_hold_begin(elem, seat, data, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gesture_hold_end(&self, seat: &Seat<State>, data: &mut State, event: &GestureHoldEndEvent) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::gesture_hold_end(elem, seat, data, event),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::gesture_hold_end(elem, seat, data, event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leave(&self, seat: &Seat<State>, data: &mut State, serial: Serial, time: u32) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => PointerTarget::leave(elem, seat, data, serial, time),
|
||||||
|
ZoomFocusTarget::Menu(elem) => PointerTarget::leave(elem, seat, data, serial, time),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TouchTarget<State> for ZoomFocusTarget {
|
||||||
|
fn down(&self, seat: &Seat<State>, data: &mut State, event: &DownEvent, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::down(elem, seat, data, event, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::down(elem, seat, data, event, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up(&self, seat: &Seat<State>, data: &mut State, event: &UpEvent, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::up(elem, seat, data, event, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::up(elem, seat, data, event, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn motion(&self, seat: &Seat<State>, data: &mut State, event: &TouchMotionEvent, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::motion(elem, seat, data, event, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::motion(elem, seat, data, event, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::frame(elem, seat, data, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cancel(&self, seat: &Seat<State>, data: &mut State, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::cancel(elem, seat, data, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shape(&self, seat: &Seat<State>, data: &mut State, event: &ShapeEvent, seq: Serial) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::shape(elem, seat, data, event, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::shape(elem, seat, data, event, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn orientation(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
data: &mut State,
|
||||||
|
event: &OrientationEvent,
|
||||||
|
seq: Serial,
|
||||||
|
) {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||||
|
ZoomFocusTarget::Menu(elem) => TouchTarget::orientation(elem, seat, data, event, seq),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IsAlive for ZoomFocusTarget {
|
||||||
|
fn alive(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
ZoomFocusTarget::Main(elem) => elem.alive(),
|
||||||
|
ZoomFocusTarget::Menu(elem) => elem.alive(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,6 +20,7 @@ pub trait PointExt<C: Coordinate> {
|
||||||
|
|
||||||
pub trait PointGlobalExt<C: Coordinate> {
|
pub trait PointGlobalExt<C: Coordinate> {
|
||||||
fn to_local(self, output: &Output) -> Point<C, Local>;
|
fn to_local(self, output: &Output) -> Point<C, Local>;
|
||||||
|
fn to_zoomed(self, output: &Output, level: f64) -> Point<C, Local>;
|
||||||
fn as_logical(self) -> Point<C, Logical>;
|
fn as_logical(self) -> Point<C, Logical>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +66,14 @@ impl<C: Coordinate> PointGlobalExt<C> for Point<C, Global> {
|
||||||
(C::from_f64(point.x), C::from_f64(point.y)).into()
|
(C::from_f64(point.x), C::from_f64(point.y)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_zoomed(self, output: &Output, level: f64) -> Point<C, Local> {
|
||||||
|
let zoomed_output_geometry = output.zoomed_geometry(level).unwrap();
|
||||||
|
let point = (self.to_f64() - zoomed_output_geometry.loc.to_f64())
|
||||||
|
.upscale(level)
|
||||||
|
.as_logical();
|
||||||
|
(C::from_f64(point.x), C::from_f64(point.y)).into()
|
||||||
|
}
|
||||||
|
|
||||||
fn as_logical(self) -> Point<C, Logical> {
|
fn as_logical(self) -> Point<C, Logical> {
|
||||||
(self.x, self.y).into()
|
(self.x, self.y).into()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,13 @@ use smithay::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use super::geometry::*;
|
pub use super::geometry::*;
|
||||||
use crate::config::{AdaptiveSync, OutputConfig, OutputState};
|
|
||||||
pub use crate::shell::{SeatExt, Shell, Workspace};
|
pub use crate::shell::{SeatExt, Shell, Workspace};
|
||||||
pub use crate::state::{Common, State};
|
pub use crate::state::{Common, State};
|
||||||
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
pub use crate::wayland::handlers::xdg_shell::popup::update_reactive_popups;
|
||||||
|
use crate::{
|
||||||
|
config::{AdaptiveSync, OutputConfig, OutputState},
|
||||||
|
shell::zoom::OutputZoomState,
|
||||||
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
|
@ -20,6 +23,8 @@ use std::{
|
||||||
|
|
||||||
pub trait OutputExt {
|
pub trait OutputExt {
|
||||||
fn geometry(&self) -> Rectangle<i32, Global>;
|
fn geometry(&self) -> Rectangle<i32, Global>;
|
||||||
|
fn zoomed_geometry(&self, level: f64) -> Option<Rectangle<i32, Global>>;
|
||||||
|
|
||||||
fn adaptive_sync(&self) -> AdaptiveSync;
|
fn adaptive_sync(&self) -> AdaptiveSync;
|
||||||
fn set_adaptive_sync(&self, vrr: AdaptiveSync);
|
fn set_adaptive_sync(&self, vrr: AdaptiveSync);
|
||||||
fn adaptive_sync_support(&self) -> Option<Support>;
|
fn adaptive_sync_support(&self) -> Option<Support>;
|
||||||
|
|
@ -52,6 +57,21 @@ impl OutputExt for Output {
|
||||||
.as_global()
|
.as_global()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn zoomed_geometry(&self, level: f64) -> Option<Rectangle<i32, Global>> {
|
||||||
|
let output_geometry = self.geometry();
|
||||||
|
|
||||||
|
let output_state = self.user_data().get::<Mutex<OutputZoomState>>()?;
|
||||||
|
let mut output_state_ref = output_state.lock().unwrap();
|
||||||
|
|
||||||
|
let focal_point = output_state_ref.focal_point().to_global(self);
|
||||||
|
let mut zoomed_output_geo = output_geometry.to_f64();
|
||||||
|
zoomed_output_geo.loc -= focal_point;
|
||||||
|
zoomed_output_geo = zoomed_output_geo.downscale(level);
|
||||||
|
zoomed_output_geo.loc += focal_point;
|
||||||
|
|
||||||
|
Some(zoomed_output_geo.to_i32_round())
|
||||||
|
}
|
||||||
|
|
||||||
fn adaptive_sync(&self) -> AdaptiveSync {
|
fn adaptive_sync(&self) -> AdaptiveSync {
|
||||||
self.user_data()
|
self.user_data()
|
||||||
.get::<Vrr>()
|
.get::<Vrr>()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue