menu: Add stack tab specific menu

This commit is contained in:
Victoria Brekenfeld 2023-12-12 15:35:23 +00:00 committed by Victoria Brekenfeld
parent 58a024ba67
commit 75990ff056
10 changed files with 197 additions and 47 deletions

View file

@ -591,6 +591,7 @@ impl CosmicStack {
pub enum Message {
DragStart,
Menu,
TabMenu(usize),
PotentialTabDragStart(usize),
Activate(usize),
Close(usize),
@ -709,6 +710,40 @@ impl Program for CosmicStackInternal {
&seat,
serial,
cursor - position.as_logical(),
true,
);
}
}
});
}
}
}
Message::TabMenu(idx) => {
if let Some((seat, serial)) = self.last_seat.lock().unwrap().clone() {
if let Some(surface) = self.windows.lock().unwrap()[idx].wl_surface() {
loop_handle.insert_idle(move |state| {
if let Some(mapped) =
state.common.shell.element_for_wl_surface(&surface).cloned()
{
if let Some(workspace) = state.common.shell.space_for_mut(&mapped) {
let position = workspace
.element_geometry(&mapped)
.unwrap()
.loc
.to_global(&workspace.output);
let mut cursor = seat
.get_pointer()
.unwrap()
.current_location()
.to_i32_round();
cursor.y -= TAB_HEIGHT;
Shell::menu_request(
state,
&surface,
&seat,
serial,
cursor - position.as_logical(),
false,
);
}
}
@ -763,6 +798,7 @@ impl Program for CosmicStackInternal {
user_data.get::<Id>().unwrap().clone(),
)
.on_press(Message::PotentialTabDragStart(i))
.on_right_click(Message::TabMenu(i))
.on_close(Message::Close(i))
}),
active,

View file

@ -121,6 +121,7 @@ pub struct Tab<Message: TabMessage> {
font: Font,
close_message: Option<Message>,
press_message: Option<Message>,
right_click_message: Option<Message>,
rule_theme: TabRuleTheme,
background_theme: TabBackgroundTheme,
active: bool,
@ -135,6 +136,7 @@ impl<Message: TabMessage> Tab<Message> {
font: cosmic::font::FONT,
close_message: None,
press_message: None,
right_click_message: None,
rule_theme: TabRuleTheme::Default,
background_theme: TabBackgroundTheme::Default,
active: false,
@ -146,6 +148,11 @@ impl<Message: TabMessage> Tab<Message> {
self
}
pub fn on_right_click(mut self, message: Message) -> Self {
self.right_click_message = Some(message);
self
}
pub fn on_close(mut self, message: Message) -> Self {
self.close_message = Some(message);
self
@ -238,6 +245,7 @@ impl<Message: TabMessage> Tab<Message> {
background: self.background_theme.into(),
elements: items,
press_message: self.press_message,
right_click_message: self.right_click_message,
}
}
}
@ -256,6 +264,7 @@ pub(super) struct TabInternal<'a, Message: TabMessage, Renderer> {
background: theme::Container,
elements: Vec<Element<'a, Message, Renderer>>,
press_message: Option<Message>,
right_click_message: Option<Message>,
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for TabInternal<'a, Message, Renderer>
@ -382,6 +391,15 @@ where
return event::Status::Captured;
}
}
if matches!(
event,
event::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right))
) {
if let Some(message) = self.right_click_message.clone() {
shell.publish(message);
return event::Status::Captured;
}
}
if matches!(
event,
event::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))

View file

@ -310,6 +310,7 @@ impl Program for CosmicWindowInternal {
&seat,
serial,
cursor - position.as_logical(),
false,
);
}
}

View file

@ -3,9 +3,13 @@ use smithay::wayland::seat::WaylandFocus;
use crate::{
config::{Action, StaticConfig},
fl,
shell::{element::CosmicMapped, grabs::ReleaseMode, Shell},
shell::{
element::{CosmicMapped, CosmicWindow},
grabs::ReleaseMode,
CosmicSurface, Shell,
},
state::{Common, State},
utils::screenshot::screenshot_window,
utils::{prelude::SeatExt, screenshot::screenshot_window},
};
use super::{Item, ResizeEdge};
@ -99,6 +103,66 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
}
}
pub fn tab_items(
stack: &CosmicMapped,
tab: &CosmicSurface,
is_tiled: bool,
config: &StaticConfig,
) -> impl Iterator<Item = Item> {
let unstack_clone_stack = stack.clone();
let unstack_clone_tab = tab.clone();
let screenshot_clone = tab.clone();
let close_clone = tab.clone();
vec![
Item::new(fl!("window-menu-unstack"), move |handle| {
let mut mapped = unstack_clone_stack.clone();
let surface = unstack_clone_tab.clone();
let _ = handle.insert_idle(move |state| {
mapped.stack_ref_mut().unwrap().remove_window(&surface);
let mapped: CosmicMapped = CosmicWindow::new(
surface,
state.common.event_loop_handle.clone(),
state.common.theme.clone(),
)
.into();
let seat = state.common.last_active_seat().clone();
let output = seat.active_output();
let workspace = state.common.shell.workspaces.active_mut(&output);
if is_tiled {
for mapped in workspace
.mapped()
.filter(|m| m.maximized_state.lock().unwrap().is_some())
.cloned()
.collect::<Vec<_>>()
.into_iter()
{
workspace.unmaximize_request(&mapped.active_window());
}
let focus_stack = workspace.focus_stack.get(&seat);
workspace
.tiling_layer
.map(mapped, Some(focus_stack.iter()), None, false);
} else {
workspace.floating_layer.map(mapped, None)
}
});
}),
Item::Separator,
Item::new(fl!("window-menu-screenshot"), move |handle| {
let tab = screenshot_clone.clone();
let _ = handle.insert_idle(move |state| screenshot_window(state, &tab));
}),
Item::Separator,
Item::new(fl!("window-menu-close"), move |_handle| {
close_clone.close();
})
.shortcut(config.get_shortcut_for_action(&Action::Close)),
]
.into_iter()
}
pub fn window_items(
window: &CosmicMapped,
is_tiled: bool,
@ -126,7 +190,7 @@ pub fn window_items(
vec![
is_stacked.then_some(
Item::new(fl!("window-menu-unstack"), move |handle| {
Item::new(fl!("window-menu-unstack-all"), move |handle| {
let mapped = unstack_clone.clone();
let _ = handle.insert_idle(move |state| {
unstack(state, &mapped);
@ -161,7 +225,8 @@ pub fn window_items(
// TODO: Where to save?
Some(Item::new(fl!("window-menu-screenshot"), move |handle| {
let mapped = screenshot_clone.clone();
let _ = handle.insert_idle(move |state| screenshot_window(state, &mapped));
let _ =
handle.insert_idle(move |state| screenshot_window(state, &mapped.active_window()));
})),
Some(Item::Separator),
Some(Item::new(fl!("window-menu-move"), move |handle| {

View file

@ -367,10 +367,11 @@ impl TilingLayout {
window: CosmicMapped,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
direction: Option<Direction>,
add_to_stack: bool,
) {
window.output_enter(&self.output, window.bbox());
window.set_bounds(self.output.geometry().size.as_logical());
self.map_internal(window, focus_stack, direction);
self.map_internal(window, focus_stack, direction, add_to_stack);
}
pub fn map_internal<'a>(
@ -378,13 +379,21 @@ impl TilingLayout {
window: impl Into<CosmicMapped>,
focus_stack: Option<impl Iterator<Item = &'a CosmicMapped> + 'a>,
direction: Option<Direction>,
add_to_stack: bool,
) {
let gaps = self.gaps();
let mut tree = self.queue.trees.back().unwrap().0.copy_clone();
let last_active = focus_stack
.and_then(|focus_stack| TilingLayout::last_active_window(&mut tree, focus_stack));
TilingLayout::map_to_tree(&mut tree, window, &self.output, last_active, direction);
TilingLayout::map_to_tree(
&mut tree,
window,
&self.output,
last_active,
direction,
add_to_stack,
);
let blocker = TilingLayout::update_positions(&self.output, &mut tree, gaps);
self.queue.push_tree(tree, ANIMATION_DURATION, blocker);
}
@ -395,6 +404,7 @@ impl TilingLayout {
output: &Output,
node: Option<(NodeId, CosmicMapped)>,
direction: Option<Direction>,
add_to_stack: bool,
) {
let window = window.into();
let new_window = Node::new(Data::Mapped {
@ -425,7 +435,7 @@ impl TilingLayout {
}
} else {
if let Some((ref node_id, mut last_active_window)) = node {
if window.is_window() && last_active_window.is_stack() {
if add_to_stack && window.is_window() && last_active_window.is_stack() {
let surface = window.active_window();
last_active_window
.stack_ref_mut()
@ -533,7 +543,7 @@ impl TilingLayout {
}
mapped.set_tiled(true);
other.map(mapped.clone(), Some(focus_stack), None);
other.map(mapped.clone(), Some(focus_stack), None, true);
return Some(KeyboardFocusTarget::Element(mapped));
}
None => {
@ -1964,6 +1974,7 @@ impl TilingLayout {
&self.output,
Some(current_node),
None,
false,
);
let node = window.tiling_node_id.lock().unwrap().clone().unwrap();
@ -2494,7 +2505,14 @@ impl TilingLayout {
}
}
_ => {
TilingLayout::map_to_tree(&mut tree, window.clone(), &self.output, None, None);
TilingLayout::map_to_tree(
&mut tree,
window.clone(),
&self.output,
None,
None,
false,
);
window
}
};

View file

@ -74,7 +74,7 @@ use self::{
CosmicWindow,
},
focus::target::KeyboardFocusTarget,
grabs::{window_items, MenuGrab, ReleaseMode, ResizeEdge, ResizeGrab},
grabs::{tab_items, window_items, Item, MenuGrab, ReleaseMode, ResizeEdge, ResizeGrab},
layout::{
floating::ResizeState,
tiling::{NodeDesc, ResizeForkGrab, TilingLayout},
@ -1508,11 +1508,12 @@ impl Shell {
.unwrap();
match target_layer {
ManagedLayer::Floating => new_workspace.floating_layer.map(mapped, None),
ManagedLayer::Tiling => {
new_workspace
.tiling_layer
.map(mapped, Option::<std::iter::Empty<_>>::None, None)
}
ManagedLayer::Tiling => new_workspace.tiling_layer.map(
mapped,
Option::<std::iter::Empty<_>>::None,
None,
false,
),
};
}
@ -1640,7 +1641,7 @@ impl Shell {
let focus_stack = workspace.focus_stack.get(&seat);
workspace
.tiling_layer
.map(mapped.clone(), Some(focus_stack.iter()), None);
.map(mapped.clone(), Some(focus_stack.iter()), None, true);
}
if should_be_fullscreen {
@ -1779,9 +1780,12 @@ impl Shell {
if window_state.layer == ManagedLayer::Floating {
to_workspace.floating_layer.map(mapped.clone(), None);
} else {
to_workspace
.tiling_layer
.map(mapped.clone(), Some(focus_stack.iter()), direction);
to_workspace.tiling_layer.map(
mapped.clone(),
Some(focus_stack.iter()),
direction,
true,
);
}
let focus_target = if let Some(f) = window_state.was_fullscreen {
@ -1919,6 +1923,7 @@ impl Shell {
seat: &Seat<State>,
serial: impl Into<Option<Serial>>,
location: Point<i32, Logical>,
target_stack: bool,
) {
let serial = serial.into();
if let Some(start_data) =
@ -1958,15 +1963,27 @@ impl Shell {
let grab = MenuGrab::new(
start_data,
seat,
window_items(
&mapped,
is_tiled,
is_stacked,
tiling_enabled,
edge,
&state.common.config.static_conf,
)
.into_iter(),
if target_stack || !is_stacked {
Box::new(window_items(
&mapped,
is_tiled,
is_stacked,
tiling_enabled,
edge,
&state.common.config.static_conf,
)) as Box<dyn Iterator<Item = Item>>
} else {
let (tab, _) = mapped
.windows()
.find(|(s, _)| s.wl_surface().as_ref() == Some(surface))
.unwrap();
Box::new(tab_items(
&mapped,
&tab,
is_tiled,
&state.common.config.static_conf,
)) as Box<dyn Iterator<Item = Item>>
},
global_position,
state.common.event_loop_handle.clone(),
state.common.theme.clone(),

View file

@ -758,7 +758,7 @@ impl Workspace {
{
self.floating_layer.unmap(&window);
self.tiling_layer
.map(window, Some(focus_stack.iter()), None)
.map(window, Some(focus_stack.iter()), None, false)
}
self.tiling_enabled = true;
}
@ -776,7 +776,7 @@ impl Workspace {
let focus_stack = self.focus_stack.get(seat);
self.floating_layer.unmap(&window);
self.tiling_layer
.map(window.clone(), Some(focus_stack.iter()), None)
.map(window.clone(), Some(focus_stack.iter()), None, false)
}
}
}