Add context menu for breadcrumbs
* Open in new tab * Open in new window
This commit is contained in:
parent
0971f6f20f
commit
7d3ae07067
4 changed files with 159 additions and 24 deletions
11
src/app.rs
11
src/app.rs
|
|
@ -1740,6 +1740,17 @@ impl Application for App {
|
|||
tab::Command::OpenInNewTab(path) => {
|
||||
commands.push(self.open_tab(Location::Path(path.clone())));
|
||||
}
|
||||
tab::Command::OpenInNewWindow(path) => match env::current_exe() {
|
||||
Ok(exe) => match process::Command::new(&exe).arg(path).spawn() {
|
||||
Ok(_child) => {}
|
||||
Err(err) => {
|
||||
log::error!("failed to execute {:?}: {}", exe, err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("failed to get current executable path: {}", err);
|
||||
}
|
||||
},
|
||||
tab::Command::Scroll(id, offset) => {
|
||||
commands.push(scrollable::scroll_to(id, offset));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -641,6 +641,9 @@ impl Application for App {
|
|||
tab::Command::OpenInNewTab(_path) => {
|
||||
log::warn!("OpenInNewTab not supported in dialog");
|
||||
}
|
||||
tab::Command::OpenInNewWindow(_path) => {
|
||||
log::warn!("OpenInNewWindow not supported in dialog");
|
||||
}
|
||||
tab::Command::Scroll(id, offset) => {
|
||||
commands.push(scrollable::scroll_to(id, offset));
|
||||
}
|
||||
|
|
|
|||
48
src/menu.rs
48
src/menu.rs
|
|
@ -11,6 +11,7 @@ use cosmic::{
|
|||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::tab::LocationMenuAction;
|
||||
use crate::{
|
||||
app::{Action, Message},
|
||||
config::TabConfig,
|
||||
|
|
@ -94,7 +95,9 @@ pub fn context_menu<'a>(
|
|||
match tab.location {
|
||||
Location::Path(_) | Location::Search(_, _) => {
|
||||
if selected > 0 {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
}
|
||||
if selected == 1 {
|
||||
children.push(menu_item(fl!("open-with"), Action::OpenWith).into());
|
||||
if selected_dir == 1 {
|
||||
|
|
@ -102,8 +105,12 @@ pub fn context_menu<'a>(
|
|||
.push(menu_item(fl!("open-in-terminal"), Action::OpenTerminal).into());
|
||||
}
|
||||
}
|
||||
children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab).into());
|
||||
children.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
|
||||
// All selected items are directories
|
||||
if selected == selected_dir {
|
||||
children.push(menu_item(fl!("open-in-new-tab"), Action::OpenInNewTab).into());
|
||||
children
|
||||
.push(menu_item(fl!("open-in-new-window"), Action::OpenInNewWindow).into());
|
||||
}
|
||||
children.push(horizontal_rule(1).into());
|
||||
children.push(menu_item(fl!("rename"), Action::Rename).into());
|
||||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
||||
|
|
@ -255,3 +262,38 @@ pub fn menu_bar<'a>(
|
|||
.spacing(4.0)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Message> {
|
||||
let children = vec![
|
||||
menu_button!(widget::text(fl!("open-in-new-tab")))
|
||||
.on_press(tab::Message::LocationMenuAction(
|
||||
LocationMenuAction::OpenInNewTab(ancestor_index),
|
||||
))
|
||||
.into(),
|
||||
menu_button!(widget::text(fl!("open-in-new-window")))
|
||||
.on_press(tab::Message::LocationMenuAction(
|
||||
LocationMenuAction::OpenInNewWindow(ancestor_index),
|
||||
))
|
||||
.into(),
|
||||
];
|
||||
|
||||
widget::container(widget::column::with_children(children))
|
||||
.padding(1)
|
||||
.style(theme::Container::custom(|theme| {
|
||||
let cosmic = theme.cosmic();
|
||||
let component = &cosmic.background.component;
|
||||
widget::container::Appearance {
|
||||
icon_color: Some(component.on.into()),
|
||||
text_color: Some(component.on.into()),
|
||||
background: Some(Background::Color(component.base.into())),
|
||||
border: Border {
|
||||
radius: 8.0.into(),
|
||||
width: 1.0,
|
||||
color: component.divider.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}))
|
||||
.width(Length::Fixed(240.0))
|
||||
.into()
|
||||
}
|
||||
|
|
|
|||
121
src/tab.rs
121
src/tab.rs
|
|
@ -1,6 +1,7 @@
|
|||
use cosmic::iced::clipboard::dnd::DndAction;
|
||||
use cosmic::iced::Border;
|
||||
use cosmic::iced_core::widget::tree;
|
||||
use cosmic::widget::menu::action::MenuAction;
|
||||
use cosmic::widget::menu::key_bind::KeyBind;
|
||||
use cosmic::widget::{vertical_space, Id, Widget};
|
||||
use cosmic::{
|
||||
|
|
@ -538,6 +539,7 @@ pub enum Command {
|
|||
FocusTextInput(widget::Id),
|
||||
OpenFile(PathBuf),
|
||||
OpenInNewTab(PathBuf),
|
||||
OpenInNewWindow(PathBuf),
|
||||
Scroll(widget::Id, AbsoluteOffset),
|
||||
DropFiles(PathBuf, ClipboardPaste),
|
||||
Timeout(Duration, Message),
|
||||
|
|
@ -553,8 +555,11 @@ pub enum Message {
|
|||
Config(TabConfig),
|
||||
ContextAction(Action),
|
||||
ContextMenu(Option<Point>),
|
||||
LocationContextMenu(Option<(Point, usize)>),
|
||||
LocationMenuAction(LocationMenuAction),
|
||||
Drag(Option<Rectangle>),
|
||||
EditLocation(Option<Location>),
|
||||
OpenInNewTab(PathBuf),
|
||||
EmptyTrash,
|
||||
GoNext,
|
||||
GoPrevious,
|
||||
|
|
@ -583,6 +588,20 @@ pub enum Message {
|
|||
ZoomOut,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum LocationMenuAction {
|
||||
OpenInNewTab(usize),
|
||||
OpenInNewWindow(usize),
|
||||
}
|
||||
|
||||
impl MenuAction for LocationMenuAction {
|
||||
type Message = Message;
|
||||
|
||||
fn message(&self) -> Self::Message {
|
||||
Message::LocationMenuAction(*self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ItemMetadata {
|
||||
Path {
|
||||
|
|
@ -805,6 +824,7 @@ impl HeadingOptions {
|
|||
pub struct Tab {
|
||||
//TODO: make more items private
|
||||
pub location: Location,
|
||||
pub location_context_menu: Option<(Point, usize)>,
|
||||
pub context_menu: Option<Point>,
|
||||
pub dialog: Option<DialogKind>,
|
||||
pub scroll_opt: Option<AbsoluteOffset>,
|
||||
|
|
@ -830,6 +850,7 @@ impl Tab {
|
|||
Self {
|
||||
location,
|
||||
context_menu: None,
|
||||
location_context_menu: None,
|
||||
dialog: None,
|
||||
scroll_opt: None,
|
||||
size_opt: Cell::new(None),
|
||||
|
|
@ -1131,6 +1152,7 @@ impl Tab {
|
|||
return commands;
|
||||
}
|
||||
self.context_menu = None;
|
||||
self.location_context_menu = None;
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
if mod_ctrl {
|
||||
|
|
@ -1174,6 +1196,7 @@ impl Tab {
|
|||
Message::Click(click_i_opt) => {
|
||||
self.selected_clicked = false;
|
||||
self.context_menu = None;
|
||||
self.location_context_menu = None;
|
||||
if click_i_opt.is_none() {
|
||||
self.clicked = click_i_opt;
|
||||
}
|
||||
|
|
@ -1249,9 +1272,37 @@ impl Tab {
|
|||
Message::ContextMenu(point_opt) => {
|
||||
self.context_menu = point_opt;
|
||||
}
|
||||
Message::LocationContextMenu(point_path_opt) => {
|
||||
self.location_context_menu = point_path_opt;
|
||||
}
|
||||
Message::LocationMenuAction(action) => {
|
||||
self.location_context_menu = None;
|
||||
let path_for_index = |ancestor_index| {
|
||||
match self.location {
|
||||
Location::Path(ref path) => Some(path),
|
||||
Location::Search(ref path, _) => Some(path),
|
||||
_ => None,
|
||||
}
|
||||
.and_then(|path| path.ancestors().nth(ancestor_index))
|
||||
.map(|path| path.to_path_buf())
|
||||
};
|
||||
match action {
|
||||
LocationMenuAction::OpenInNewTab(ancestor_index) => {
|
||||
if let Some(path) = path_for_index(ancestor_index) {
|
||||
commands.push(Command::OpenInNewTab(path));
|
||||
}
|
||||
}
|
||||
LocationMenuAction::OpenInNewWindow(ancestor_index) => {
|
||||
if let Some(path) = path_for_index(ancestor_index) {
|
||||
commands.push(Command::OpenInNewWindow(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::Drag(rect_opt) => match rect_opt {
|
||||
Some(rect) => {
|
||||
self.context_menu = None;
|
||||
self.location_context_menu = None;
|
||||
self.select_rect(rect, mod_ctrl, mod_shift);
|
||||
if self.select_focus.take().is_some() {
|
||||
// Unfocus currently focused button
|
||||
|
|
@ -1266,6 +1317,9 @@ impl Tab {
|
|||
}
|
||||
self.edit_location = edit_location;
|
||||
}
|
||||
Message::OpenInNewTab(path) => {
|
||||
commands.push(Command::OpenInNewTab(path));
|
||||
}
|
||||
Message::EmptyTrash => {
|
||||
commands.push(Command::EmptyTrash);
|
||||
}
|
||||
|
|
@ -1788,12 +1842,15 @@ impl Tab {
|
|||
//TODO: allow editing other locations
|
||||
}
|
||||
}
|
||||
} else if let Location::Path(_) = &self.location {
|
||||
} else if let Location::Path(path) = &self.location {
|
||||
row = row.push(
|
||||
widget::button(widget::icon::from_name("edit-symbolic").size(16))
|
||||
.on_press(Message::EditLocation(Some(self.location.clone())))
|
||||
.padding(space_xxs)
|
||||
.style(theme::Button::Icon),
|
||||
crate::mouse_area::MouseArea::new(
|
||||
widget::button(widget::icon::from_name("edit-symbolic").size(16))
|
||||
.padding(space_xxs)
|
||||
.style(theme::Button::Icon),
|
||||
)
|
||||
.on_press(move |_| Message::EditLocation(Some(self.location.clone())))
|
||||
.on_middle_press(move |_| Message::OpenInNewTab(path.clone())),
|
||||
);
|
||||
} else if let Location::Search(_, term) = &self.location {
|
||||
row = row.push(
|
||||
|
|
@ -1815,8 +1872,7 @@ impl Tab {
|
|||
match &self.location {
|
||||
Location::Path(path) | Location::Search(path, ..) => {
|
||||
let home_dir = crate::home_dir();
|
||||
for ancestor in path.ancestors() {
|
||||
let ancestor = ancestor.to_path_buf();
|
||||
for (index, ancestor) in path.ancestors().enumerate() {
|
||||
let mut found_home = false;
|
||||
let mut row = widget::row::with_capacity(2)
|
||||
.align_items(Alignment::Center)
|
||||
|
|
@ -1826,8 +1882,11 @@ impl Tab {
|
|||
Some(name) => {
|
||||
if ancestor == home_dir {
|
||||
row = row.push(
|
||||
widget::icon::icon(folder_icon_symbolic(&ancestor, 16))
|
||||
.size(16),
|
||||
widget::icon::icon(folder_icon_symbolic(
|
||||
&ancestor.to_path_buf(),
|
||||
16,
|
||||
))
|
||||
.size(16),
|
||||
);
|
||||
found_home = true;
|
||||
fl!("home")
|
||||
|
|
@ -1857,19 +1916,31 @@ impl Tab {
|
|||
row = row.push(widget::text(name));
|
||||
}
|
||||
|
||||
children.push(
|
||||
let mut mouse_area = crate::mouse_area::MouseArea::new(
|
||||
widget::button(row)
|
||||
.padding(space_xxxs)
|
||||
.on_press(Message::Location(match &self.location {
|
||||
Location::Path(_) => Location::Path(ancestor),
|
||||
Location::Search(_, term) => {
|
||||
Location::Search(ancestor, term.clone())
|
||||
}
|
||||
other => other.clone(),
|
||||
}))
|
||||
.style(theme::Button::Link)
|
||||
.into(),
|
||||
);
|
||||
.style(theme::Button::Link),
|
||||
)
|
||||
.on_press(move |_| {
|
||||
Message::Location(match &self.location {
|
||||
Location::Path(_) => Location::Path(ancestor.to_path_buf()),
|
||||
Location::Search(_, term) => {
|
||||
Location::Search(ancestor.to_path_buf(), term.clone())
|
||||
}
|
||||
other => other.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
if self.location_context_menu.is_some() {
|
||||
mouse_area = mouse_area
|
||||
.on_right_press(move |_point_opt| Message::LocationContextMenu(None))
|
||||
} else {
|
||||
mouse_area = mouse_area.on_right_press(move |point_opt| {
|
||||
Message::LocationContextMenu(point_opt.map(|point| (point, index)))
|
||||
})
|
||||
}
|
||||
|
||||
children.push(mouse_area.into());
|
||||
|
||||
if found_home {
|
||||
break;
|
||||
|
|
@ -1897,7 +1968,15 @@ impl Tab {
|
|||
for child in children {
|
||||
row = row.push(child);
|
||||
}
|
||||
row.into()
|
||||
|
||||
let mut popover = widget::popover(row);
|
||||
if let Some((point, ancestor_index)) = self.location_context_menu {
|
||||
popover = popover
|
||||
.popup(menu::location_context_menu(ancestor_index))
|
||||
.position(widget::popover::Position::Point(point))
|
||||
}
|
||||
|
||||
popover.into()
|
||||
}
|
||||
|
||||
pub fn empty_view(&self, has_hidden: bool) -> Element<Message> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue