diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index b115e2b0..2b67f61f 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -10,6 +10,7 @@ use calloop::LoopHandle; use cosmic::{ iced::widget as iced_widget, iced_core::{alignment, Color, Length}, + iced_runtime::Command, iced_widget::rule::FillMode, theme, widget as cosmic_widget, Element as CosmicElement, }; @@ -67,8 +68,9 @@ pub struct CosmicStackInternal { activated: Arc, group_focused: Arc, previous_keyboard: Arc, - pointer_entered: Option>, + pointer_entered: Arc, previous_pointer: Arc, + last_seat: Arc, Serial)>>>, last_location: Arc, Serial, u32)>>>, geometry: Arc>>>, mask: Arc>>, @@ -76,23 +78,15 @@ pub struct CosmicStackInternal { impl CosmicStackInternal { pub fn swap_focus(&self, focus: Focus) -> Focus { - if let Some(pointer_entered) = self.pointer_entered.as_ref() { - unsafe { - std::mem::transmute::( - pointer_entered.swap(focus as u8, Ordering::SeqCst), - ) - } - } else { - Focus::Window + unsafe { + std::mem::transmute::( + self.pointer_entered.swap(focus as u8, Ordering::SeqCst), + ) } } pub fn current_focus(&self) -> Focus { - if let Some(pointer_entered) = self.pointer_entered.as_ref() { - unsafe { std::mem::transmute::(pointer_entered.load(Ordering::SeqCst)) } - } else { - Focus::Window - } + unsafe { std::mem::transmute::(self.pointer_entered.load(Ordering::SeqCst)) } } } @@ -127,8 +121,9 @@ impl CosmicStack { activated: Arc::new(AtomicBool::new(false)), group_focused: Arc::new(AtomicBool::new(false)), previous_keyboard: Arc::new(AtomicUsize::new(0)), - pointer_entered: None, + pointer_entered: Arc::new(AtomicU8::new(Focus::None as u8)), previous_pointer: Arc::new(AtomicUsize::new(0)), + last_seat: Arc::new(Mutex::new(None)), last_location: Arc::new(Mutex::new(None)), geometry: Arc::new(Mutex::new(None)), mask: Arc::new(Mutex::new(None)), @@ -190,9 +185,18 @@ impl CosmicStack { let result = self.0.with_program(|p| match direction { FocusDirection::Left => { if !p.group_focused.load(Ordering::SeqCst) { - p.active - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| val.checked_sub(1)) - .is_ok() + 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.previous_pointer.store(old, Ordering::SeqCst); + true + } else { + false + } } else { false } @@ -200,15 +204,22 @@ impl CosmicStack { FocusDirection::Right => { if !p.group_focused.load(Ordering::SeqCst) { let max = p.windows.lock().unwrap().len(); - p.active - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| { - if val < max - 1 { - Some(val + 1) - } else { - None - } - }) - .is_ok() + if let Ok(old) = + p.active + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| { + if val < max - 1 { + Some(val + 1) + } else { + None + } + }) + { + p.previous_keyboard.store(old, Ordering::SeqCst); + p.previous_pointer.store(old, Ordering::SeqCst); + true + } else { + false + } } else { false } @@ -312,9 +323,13 @@ impl CosmicStack { if let Some(previous) = windows.get(previous) { KeyboardTarget::leave(previous, seat, data, serial); } - seat.get_keyboard().unwrap().with_pressed_keysyms(|syms| { - KeyboardTarget::enter(&windows[active], seat, data, syms, serial) - }) + KeyboardTarget::enter( + &windows[active], + seat, + data, + Vec::new(), /* TODO */ + serial, + ) } active }) @@ -367,8 +382,49 @@ impl CosmicStack { } } +#[derive(Debug, Clone, Copy)] +pub enum Message { + DragStart, + Activate(usize), + Close(usize), +} + impl Program for CosmicStackInternal { - type Message = (); + type Message = Message; + + fn update( + &mut self, + message: Self::Message, + loop_handle: &LoopHandle<'static, crate::state::Data>, + ) -> Command { + match message { + Message::DragStart => { + if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() { + if let Some(surface) = self.windows.lock().unwrap() + [self.active.load(Ordering::SeqCst)] + .wl_surface() + { + loop_handle.insert_idle(move |data| { + Shell::move_request(&mut data.state, &surface, &seat, serial); + }); + } + } + } + Message::Activate(idx) => { + if self.windows.lock().unwrap().get(idx).is_some() { + let old = self.active.swap(idx, Ordering::SeqCst); + self.previous_keyboard.store(old, Ordering::SeqCst); + self.previous_pointer.store(old, Ordering::SeqCst); + } + } + Message::Close(idx) => { + if let Some(val) = self.windows.lock().unwrap().get(idx) { + val.close() + } + } + } + Command::none() + } fn view(&self) -> CosmicElement<'_, Self::Message> { let windows = self.windows.lock().unwrap(); @@ -402,6 +458,8 @@ impl Program for CosmicStackInternal { .apply(iced_widget::container) .padding([4, 24]) .center_y() + .apply(iced_widget::mouse_area) + .on_press(Message::DragStart) .into()]; const ACTIVE_TAB_WIDTH: i32 = 140; @@ -471,7 +529,7 @@ impl Program for CosmicStackInternal { .into(), ); - let text_width = tab_width - if tab_width > 125 { 64 } else { 40 }; + let text_width = tab_width - if tab_width > 125 { 72 } else { 40 }; if text_width > 0 { tab_elements.push( cosmic_widget::text(title) @@ -494,6 +552,9 @@ impl Program for CosmicStackInternal { cosmic_widget::icon("window-close-symbolic", 16) .force_svg(true) .style(theme::Svg::Symbolic) + .apply(iced_widget::button) + .style(theme::Button::Text) + .on_press(Message::Close(i)) .apply(iced_widget::container) .height(Length::Fill) .center_y() @@ -534,6 +595,8 @@ impl Program for CosmicStackInternal { } else { theme::Container::Transparent }) + .apply(iced_widget::mouse_area) + .on_press(Message::Activate(i)) .into(), ) } @@ -596,7 +659,12 @@ impl Program for CosmicStackInternal { elements.push(tabs.into()); } - elements.push(iced_widget::horizontal_space(64).into()); + elements.push( + iced_widget::horizontal_space(64) + .apply(iced_widget::mouse_area) + .on_press(Message::DragStart) + .into(), + ); iced_widget::row(elements) .height(TAB_HEIGHT as u16) @@ -861,47 +929,52 @@ impl KeyboardTarget for CosmicStack { impl PointerTarget for CosmicStack { fn enter(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + let mut event = event.clone(); + event.location.y -= TAB_HEIGHT as f64; if self.0.with_program(|p| { - if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] - .user_data() - .get::() - { + let active_window = &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)]; + if let Some(sessions) = active_window.user_data().get::() { for session in &*sessions.0.borrow() { session.cursor_enter(seat, InputType::Pointer) } } - if event.location.y < TAB_HEIGHT as f64 { - let focus = p.swap_focus(Focus::Header); + if (event.location.y - active_window.geometry().loc.y as f64) < 0. { + let previous = p.swap_focus(Focus::Header); + if previous == Focus::Window { + PointerTarget::leave(active_window, seat, data, event.serial, event.time); + } true } else { - let focus = p.swap_focus(Focus::Window); + p.swap_focus(Focus::Window); *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); let active = p.active.load(Ordering::SeqCst); p.previous_pointer.store(active, Ordering::SeqCst); - PointerTarget::enter( - &p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)], - seat, - data, - event, - ); + PointerTarget::enter(active_window, seat, data, &event); false } }) { - PointerTarget::enter(&self.0, seat, data, event) + event.location.y += TAB_HEIGHT as f64; + event.location -= self.0.with_program(|p| { + p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] + .geometry() + .loc + .to_f64() + }); + PointerTarget::enter(&self.0, seat, data, &event) } } fn motion(&self, seat: &Seat, data: &mut State, event: &MotionEvent) { + let mut event = event.clone(); + event.location.y -= TAB_HEIGHT as f64; let active = self.pointer_leave_if_previous(seat, data, event.serial, event.time, event.location); if let Some((previous, next)) = self.0.with_program(|p| { - if let Some(sessions) = p.windows.lock().unwrap()[p.active.load(Ordering::SeqCst)] - .user_data() - .get::() - { + let active_window = &p.windows.lock().unwrap()[active]; + if let Some(sessions) = active_window.user_data().get::() { for session in &*sessions.0.borrow() { let buffer_loc = (event.location.x, event.location.y); // we always screencast windows at 1x1 scale if let Some((geo, hotspot)) = @@ -912,35 +985,34 @@ impl PointerTarget for CosmicStack { } } - if event.location.y < TAB_HEIGHT as f64 { + if (event.location.y - active_window.geometry().loc.y as f64) < 0. { let previous = p.swap_focus(Focus::Header); if previous == Focus::Window { - PointerTarget::leave( - &p.windows.lock().unwrap()[active], - seat, - data, - event.serial, - event.time, - ); + PointerTarget::leave(active_window, seat, data, event.serial, event.time); } Some((previous, Focus::Header)) } else { - let mut event = event.clone(); - event.location.y -= TAB_HEIGHT as f64; + *p.last_location.lock().unwrap() = Some((event.location, event.serial, event.time)); let previous = p.swap_focus(Focus::Window); if previous != Focus::Window { - PointerTarget::enter(&p.windows.lock().unwrap()[active], seat, data, &event); + PointerTarget::enter(active_window, seat, data, &event); } else { - PointerTarget::motion(&p.windows.lock().unwrap()[active], seat, data, &event); + PointerTarget::motion(active_window, seat, data, &event); } Some((previous, Focus::Window)) } }) { + event.location.y += TAB_HEIGHT as f64; + event.location -= self + .0 + .with_program(|p| p.windows.lock().unwrap()[active].geometry().loc.to_f64()); match (previous, next) { - (Focus::Header, Focus::Header) => PointerTarget::motion(&self.0, seat, data, event), - (_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, event), + (Focus::Header, Focus::Header) => { + PointerTarget::motion(&self.0, seat, data, &event) + } + (_, Focus::Header) => PointerTarget::enter(&self.0, seat, data, &event), (Focus::Header, _) => { PointerTarget::leave(&self.0, seat, data, event.serial, event.time) } @@ -967,7 +1039,12 @@ impl PointerTarget for CosmicStack { } match self.0.with_program(|p| p.current_focus()) { - Focus::Header => PointerTarget::button(&self.0, seat, data, event), + Focus::Header => { + self.0.with_program(|p| { + *p.last_seat.lock().unwrap() = Some((seat.clone(), event.serial)); + }); + PointerTarget::button(&self.0, seat, data, event) + } Focus::Window => { if self.0.with_program(|p| { PointerTarget::button( @@ -1035,7 +1112,6 @@ impl PointerTarget for CosmicStack { p.swap_focus(Focus::None) }); - assert!(previous != Focus::None); match previous { Focus::Header => PointerTarget::leave(&self.0, seat, data, serial, time),