menu: Add stack tab specific menu
This commit is contained in:
parent
58a024ba67
commit
75990ff056
10 changed files with 197 additions and 47 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ impl Program for CosmicWindowInternal {
|
|||
&seat,
|
||||
serial,
|
||||
cursor - position.as_logical(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue