stack: Remember previous position during focus navigation
This commit is contained in:
parent
e6f563d04e
commit
e116f20396
6 changed files with 137 additions and 73 deletions
|
|
@ -22,7 +22,7 @@ use crate::{
|
||||||
tiling::{NodeDesc, SwapWindowGrab, TilingLayout},
|
tiling::{NodeDesc, SwapWindowGrab, TilingLayout},
|
||||||
},
|
},
|
||||||
zoom::ZoomState,
|
zoom::ZoomState,
|
||||||
SeatExt, Trigger,
|
LastModifierChange, SeatExt, Trigger,
|
||||||
},
|
},
|
||||||
utils::{float::NextDown, prelude::*, quirks::workspace_overview_is_open},
|
utils::{float::NextDown, prelude::*, quirks::workspace_overview_is_open},
|
||||||
wayland::{
|
wayland::{
|
||||||
|
|
@ -219,6 +219,7 @@ impl State {
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
let time = Event::time_msec(&event);
|
let time = Event::time_msec(&event);
|
||||||
let keyboard = seat.get_keyboard().unwrap();
|
let keyboard = seat.get_keyboard().unwrap();
|
||||||
|
let previous_modifiers = keyboard.modifier_state();
|
||||||
if let Some((action, pattern)) = keyboard
|
if let Some((action, pattern)) = keyboard
|
||||||
.input(
|
.input(
|
||||||
self,
|
self,
|
||||||
|
|
@ -227,6 +228,15 @@ impl State {
|
||||||
serial,
|
serial,
|
||||||
time,
|
time,
|
||||||
|data, modifiers, handle| {
|
|data, modifiers, handle| {
|
||||||
|
if previous_modifiers != *modifiers {
|
||||||
|
*seat
|
||||||
|
.user_data()
|
||||||
|
.get::<LastModifierChange>()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.unwrap() = Some(serial);
|
||||||
|
}
|
||||||
Self::filter_keyboard_input(
|
Self::filter_keyboard_input(
|
||||||
data, &event, &seat, modifiers, handle, serial,
|
data, &event, &seat, modifiers, handle, serial,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -319,9 +319,14 @@ impl CosmicMapped {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_focus(&self, direction: FocusDirection, swap: Option<NodeDesc>) -> bool {
|
pub fn handle_focus(
|
||||||
|
&self,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
direction: FocusDirection,
|
||||||
|
swap: Option<NodeDesc>,
|
||||||
|
) -> bool {
|
||||||
if let CosmicMappedInternal::Stack(stack) = &self.element {
|
if let CosmicMappedInternal::Stack(stack) = &self.element {
|
||||||
stack.handle_focus(direction, swap)
|
stack.handle_focus(seat, direction, swap)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ pub struct CosmicStackInternal {
|
||||||
active: Arc<AtomicUsize>,
|
active: Arc<AtomicUsize>,
|
||||||
activated: Arc<AtomicBool>,
|
activated: Arc<AtomicBool>,
|
||||||
group_focused: Arc<AtomicBool>,
|
group_focused: Arc<AtomicBool>,
|
||||||
|
previous_index: Arc<Mutex<Option<(Serial, usize)>>>,
|
||||||
scroll_to_focus: Arc<AtomicBool>,
|
scroll_to_focus: Arc<AtomicBool>,
|
||||||
previous_keyboard: Arc<AtomicUsize>,
|
previous_keyboard: Arc<AtomicUsize>,
|
||||||
pointer_entered: Arc<AtomicU8>,
|
pointer_entered: Arc<AtomicU8>,
|
||||||
|
|
@ -148,6 +149,7 @@ impl CosmicStack {
|
||||||
active: Arc::new(AtomicUsize::new(0)),
|
active: Arc::new(AtomicUsize::new(0)),
|
||||||
activated: Arc::new(AtomicBool::new(false)),
|
activated: Arc::new(AtomicBool::new(false)),
|
||||||
group_focused: Arc::new(AtomicBool::new(false)),
|
group_focused: Arc::new(AtomicBool::new(false)),
|
||||||
|
previous_index: Arc::new(Mutex::new(None)),
|
||||||
scroll_to_focus: Arc::new(AtomicBool::new(false)),
|
scroll_to_focus: Arc::new(AtomicBool::new(false)),
|
||||||
previous_keyboard: Arc::new(AtomicUsize::new(0)),
|
previous_keyboard: Arc::new(AtomicUsize::new(0)),
|
||||||
pointer_entered: Arc::new(AtomicU8::new(0)),
|
pointer_entered: Arc::new(AtomicU8::new(0)),
|
||||||
|
|
@ -255,75 +257,117 @@ impl CosmicStack {
|
||||||
self.0.with_program(|p| p.windows.lock().unwrap().len())
|
self.0.with_program(|p| p.windows.lock().unwrap().len())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_focus(&self, direction: FocusDirection, swap: Option<NodeDesc>) -> bool {
|
pub fn handle_focus(
|
||||||
let result = self.0.with_program(|p| match direction {
|
&self,
|
||||||
FocusDirection::Left => {
|
seat: &Seat<State>,
|
||||||
if !p.group_focused.load(Ordering::SeqCst) {
|
direction: FocusDirection,
|
||||||
if let Ok(old) =
|
swap: Option<NodeDesc>,
|
||||||
p.active
|
) -> bool {
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
|
let (result, update) = self.0.with_program(|p| {
|
||||||
val.checked_sub(1)
|
let last_mod_serial = seat.last_modifier_change();
|
||||||
})
|
let mut prev_idx = p.previous_index.lock().unwrap();
|
||||||
{
|
if !prev_idx.is_some_and(|(serial, _)| Some(serial) == last_mod_serial) {
|
||||||
p.previous_keyboard.store(old, Ordering::SeqCst);
|
*prev_idx = last_mod_serial.map(|s| (s, p.active.load(Ordering::SeqCst)));
|
||||||
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
}
|
||||||
true
|
|
||||||
|
match direction {
|
||||||
|
FocusDirection::Left => {
|
||||||
|
if !p.group_focused.load(Ordering::SeqCst) {
|
||||||
|
if let Ok(old) =
|
||||||
|
p.active
|
||||||
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
|
||||||
|
val.checked_sub(1)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
p.previous_keyboard.store(old, Ordering::SeqCst);
|
||||||
|
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
||||||
|
(true, true)
|
||||||
|
} else {
|
||||||
|
let new = prev_idx.unwrap().1;
|
||||||
|
let old = p.active.swap(new, Ordering::SeqCst);
|
||||||
|
if old != new {
|
||||||
|
p.previous_keyboard.store(old, Ordering::SeqCst);
|
||||||
|
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
||||||
|
(false, true)
|
||||||
|
} else {
|
||||||
|
(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
(false, false)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
FocusDirection::Right => {
|
||||||
FocusDirection::Right => {
|
if !p.group_focused.load(Ordering::SeqCst) {
|
||||||
if !p.group_focused.load(Ordering::SeqCst) {
|
let max = p.windows.lock().unwrap().len();
|
||||||
let max = p.windows.lock().unwrap().len();
|
if let Ok(old) =
|
||||||
if let Ok(old) =
|
p.active
|
||||||
p.active
|
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
|
||||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
|
if val < max - 1 {
|
||||||
if val < max - 1 {
|
Some(val + 1)
|
||||||
Some(val + 1)
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
}
|
||||||
}
|
})
|
||||||
})
|
{
|
||||||
{
|
p.previous_keyboard.store(old, Ordering::SeqCst);
|
||||||
p.previous_keyboard.store(old, Ordering::SeqCst);
|
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
||||||
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
(true, true)
|
||||||
true
|
} else {
|
||||||
|
let new = prev_idx.unwrap().1;
|
||||||
|
let old = p.active.swap(new, Ordering::SeqCst);
|
||||||
|
if old != new {
|
||||||
|
p.previous_keyboard.store(old, Ordering::SeqCst);
|
||||||
|
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
||||||
|
(false, true)
|
||||||
|
} else {
|
||||||
|
(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
(false, false)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
FocusDirection::Out if swap.is_none() => {
|
||||||
FocusDirection::Out if swap.is_none() => {
|
if !p.group_focused.swap(true, Ordering::SeqCst) {
|
||||||
if !p.group_focused.swap(true, Ordering::SeqCst) {
|
p.windows.lock().unwrap().iter().for_each(|w| {
|
||||||
p.windows.lock().unwrap().iter().for_each(|w| {
|
w.set_activated(false);
|
||||||
w.set_activated(false);
|
w.send_configure();
|
||||||
w.send_configure();
|
});
|
||||||
});
|
(true, true)
|
||||||
true
|
} else {
|
||||||
} else {
|
(false, false)
|
||||||
false
|
}
|
||||||
}
|
}
|
||||||
}
|
FocusDirection::In if swap.is_none() => {
|
||||||
FocusDirection::In if swap.is_none() => {
|
if !p.group_focused.swap(false, Ordering::SeqCst) {
|
||||||
if !p.group_focused.swap(false, Ordering::SeqCst) {
|
p.windows.lock().unwrap().iter().for_each(|w| {
|
||||||
p.windows.lock().unwrap().iter().for_each(|w| {
|
w.set_activated(true);
|
||||||
w.set_activated(true);
|
w.send_configure();
|
||||||
w.send_configure();
|
});
|
||||||
});
|
(true, true)
|
||||||
true
|
} else {
|
||||||
} else {
|
(false, false)
|
||||||
false
|
}
|
||||||
}
|
}
|
||||||
|
FocusDirection::Up | FocusDirection::Down => {
|
||||||
|
if !p.group_focused.load(Ordering::SeqCst) {
|
||||||
|
let new = prev_idx.unwrap().1;
|
||||||
|
let old = p.active.swap(new, Ordering::SeqCst);
|
||||||
|
if old != new {
|
||||||
|
p.previous_keyboard.store(old, Ordering::SeqCst);
|
||||||
|
p.scroll_to_focus.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
(false, true)
|
||||||
|
} else {
|
||||||
|
(false, false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (false, false),
|
||||||
}
|
}
|
||||||
_ => false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if result {
|
if update {
|
||||||
self.0
|
self.0
|
||||||
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
|
.resize(Size::from((self.active().geometry().size.w, TAB_HEIGHT)));
|
||||||
self.0.force_update();
|
self.0.force_update();
|
||||||
|
|
|
||||||
|
|
@ -1826,16 +1826,6 @@ impl TilingLayout {
|
||||||
|
|
||||||
let (last_node_id, data) = focused;
|
let (last_node_id, data) = focused;
|
||||||
|
|
||||||
// stacks may handle focus internally
|
|
||||||
if let FocusedNodeData::Window(window) = data.clone() {
|
|
||||||
if window.handle_focus(
|
|
||||||
direction,
|
|
||||||
swap_desc.clone().filter(|desc| desc.node == last_node_id),
|
|
||||||
) {
|
|
||||||
return FocusResult::Handled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if direction == FocusDirection::In {
|
if direction == FocusDirection::In {
|
||||||
if swap_desc
|
if swap_desc
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
||||||
|
|
@ -3176,7 +3176,7 @@ impl Shell {
|
||||||
return FocusResult::None;
|
return FocusResult::None;
|
||||||
};
|
};
|
||||||
|
|
||||||
if focused.handle_focus(direction, None) {
|
if focused.handle_focus(seat, direction, None) {
|
||||||
return FocusResult::Handled;
|
return FocusResult::Handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
||||||
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Time, Transform},
|
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
||||||
wayland::compositor::with_states,
|
wayland::compositor::with_states,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
@ -172,6 +172,9 @@ struct ActiveOutput(pub Mutex<Output>);
|
||||||
/// The output which currently has keyboard focus
|
/// The output which currently has keyboard focus
|
||||||
struct FocusedOutput(pub Mutex<Option<Output>>);
|
struct FocusedOutput(pub Mutex<Option<Output>>);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
||||||
|
|
||||||
pub fn create_seat(
|
pub fn create_seat(
|
||||||
dh: &DisplayHandle,
|
dh: &DisplayHandle,
|
||||||
seat_state: &mut SeatState<State>,
|
seat_state: &mut SeatState<State>,
|
||||||
|
|
@ -186,6 +189,7 @@ pub fn create_seat(
|
||||||
userdata.insert_if_missing(SupressedKeys::default);
|
userdata.insert_if_missing(SupressedKeys::default);
|
||||||
userdata.insert_if_missing(SupressedButtons::default);
|
userdata.insert_if_missing(SupressedButtons::default);
|
||||||
userdata.insert_if_missing(ModifiersShortcutQueue::default);
|
userdata.insert_if_missing(ModifiersShortcutQueue::default);
|
||||||
|
userdata.insert_if_missing(LastModifierChange::default);
|
||||||
userdata.insert_if_missing_threadsafe(SeatMoveGrabState::default);
|
userdata.insert_if_missing_threadsafe(SeatMoveGrabState::default);
|
||||||
userdata.insert_if_missing_threadsafe(SeatMenuGrabState::default);
|
userdata.insert_if_missing_threadsafe(SeatMenuGrabState::default);
|
||||||
userdata.insert_if_missing_threadsafe(CursorState::default);
|
userdata.insert_if_missing_threadsafe(CursorState::default);
|
||||||
|
|
@ -241,6 +245,7 @@ pub trait SeatExt {
|
||||||
fn supressed_keys(&self) -> &SupressedKeys;
|
fn supressed_keys(&self) -> &SupressedKeys;
|
||||||
fn supressed_buttons(&self) -> &SupressedButtons;
|
fn supressed_buttons(&self) -> &SupressedButtons;
|
||||||
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
||||||
|
fn last_modifier_change(&self) -> Option<Serial>;
|
||||||
|
|
||||||
fn cursor_geometry(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -315,6 +320,16 @@ impl SeatExt for Seat<State> {
|
||||||
self.user_data().get::<ModifiersShortcutQueue>().unwrap()
|
self.user_data().get::<ModifiersShortcutQueue>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn last_modifier_change(&self) -> Option<Serial> {
|
||||||
|
*self
|
||||||
|
.user_data()
|
||||||
|
.get::<LastModifierChange>()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn cursor_geometry(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
loc: impl Into<Point<f64, Buffer>>,
|
loc: impl Into<Point<f64, Buffer>>,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue