On wayland, place context menus into popups, fixes #1090
This commit is contained in:
parent
050e043867
commit
605f44763b
7 changed files with 211 additions and 62 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1484,6 +1484,7 @@ dependencies = [
|
||||||
"bzip2",
|
"bzip2",
|
||||||
"chrono",
|
"chrono",
|
||||||
"compio",
|
"compio",
|
||||||
|
"cosmic-client-toolkit",
|
||||||
"cosmic-mime-apps",
|
"cosmic-mime-apps",
|
||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ icu = { version = "1.5.0", features = [
|
||||||
"compiled_data",
|
"compiled_data",
|
||||||
"icu_datetime_experimental",
|
"icu_datetime_experimental",
|
||||||
] }
|
] }
|
||||||
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "178eb0b", optional = true }
|
||||||
cosmic-mime-apps = { git = "https://github.com/pop-os/cosmic-mime-apps.git", optional = true }
|
cosmic-mime-apps = { git = "https://github.com/pop-os/cosmic-mime-apps.git", optional = true }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
|
@ -106,7 +107,7 @@ io-uring = ["compio/io-uring", "dep:io-uring"]
|
||||||
io-uring-bindgen = ["io-uring?/bindgen"]
|
io-uring-bindgen = ["io-uring?/bindgen"]
|
||||||
jemalloc = ["dep:tikv-jemallocator"]
|
jemalloc = ["dep:tikv-jemallocator"]
|
||||||
notify = ["dep:notify-rust"]
|
notify = ["dep:notify-rust"]
|
||||||
wayland = ["libcosmic/wayland", "dep:wayland-client"]
|
wayland = ["libcosmic/wayland", "dep:cctk", "dep:wayland-client"]
|
||||||
wgpu = ["libcosmic/wgpu"]
|
wgpu = ["libcosmic/wgpu"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
|
||||||
134
src/app.rs
134
src/app.rs
|
|
@ -602,11 +602,12 @@ pub struct MounterData(MounterKey, MounterItem);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum WindowKind {
|
pub enum WindowKind {
|
||||||
|
ContextMenu(Entity, widget::Id),
|
||||||
Desktop(Entity),
|
Desktop(Entity),
|
||||||
DesktopViewOptions,
|
DesktopViewOptions,
|
||||||
Dialogs(widget::Id),
|
Dialogs(widget::Id),
|
||||||
Preview(Option<Entity>, PreviewKind),
|
|
||||||
FileDialog(Option<Vec<PathBuf>>),
|
FileDialog(Option<Vec<PathBuf>>),
|
||||||
|
Preview(Option<Entity>, PreviewKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WatcherWrapper {
|
pub struct WatcherWrapper {
|
||||||
|
|
@ -671,7 +672,7 @@ pub struct App {
|
||||||
surface_names: HashMap<WindowId, String>,
|
surface_names: HashMap<WindowId, String>,
|
||||||
toasts: widget::toaster::Toasts<Message>,
|
toasts: widget::toaster::Toasts<Message>,
|
||||||
watcher_opt: Option<(Debouncer<RecommendedWatcher, FileIdMap>, HashSet<PathBuf>)>,
|
watcher_opt: Option<(Debouncer<RecommendedWatcher, FileIdMap>, HashSet<PathBuf>)>,
|
||||||
window_id_opt: Option<window::Id>,
|
pub(crate) window_id_opt: Option<window::Id>,
|
||||||
windows: HashMap<window::Id, WindowKind>,
|
windows: HashMap<window::Id, WindowKind>,
|
||||||
nav_dnd_hover: Option<(Location, Instant)>,
|
nav_dnd_hover: Option<(Location, Instant)>,
|
||||||
tab_dnd_hover: Option<(Entity, Instant)>,
|
tab_dnd_hover: Option<(Entity, Instant)>,
|
||||||
|
|
@ -1120,9 +1121,20 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_window(&mut self, id: &window::Id) {
|
fn remove_window(&mut self, id: &window::Id) {
|
||||||
if let Some(WindowKind::Desktop(entity)) = self.windows.remove(id) {
|
if let Some(window_kind) = self.windows.remove(id) {
|
||||||
// Remove the tab from the tab model
|
match window_kind {
|
||||||
self.tab_model.remove(entity);
|
WindowKind::ContextMenu(entity, _) => {
|
||||||
|
// Close context menu
|
||||||
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||||
|
tab.context_menu = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowKind::Desktop(entity) => {
|
||||||
|
// Remove the tab from the tab model
|
||||||
|
self.tab_model.remove(entity);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2410,8 +2422,10 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||||
if tab.context_menu.is_some() {
|
if tab.context_menu.is_some() {
|
||||||
tab.context_menu = None;
|
return self.update(Message::TabMessage(
|
||||||
return Task::none();
|
Some(entity),
|
||||||
|
tab::Message::ContextMenu(None),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if tab.edit_location.is_some() {
|
if tab.edit_location.is_some() {
|
||||||
|
|
@ -3683,12 +3697,26 @@ impl Application for App {
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
Message::TabActivate(entity) => {
|
Message::TabActivate(entity) => {
|
||||||
self.tab_model.activate(entity);
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
|
// Close old context menu
|
||||||
|
let active = self.tab_model.active();
|
||||||
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(active) {
|
||||||
|
if tab.context_menu.is_some() {
|
||||||
|
tasks.push(self.update(Message::TabMessage(
|
||||||
|
Some(active),
|
||||||
|
tab::Message::ContextMenu(None),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activate new tab
|
||||||
|
self.tab_model.activate(entity);
|
||||||
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
if let Some(tab) = self.tab_model.data::<Tab>(entity) {
|
||||||
self.activate_nav_model_location(&tab.location.clone());
|
self.activate_nav_model_location(&tab.location.clone());
|
||||||
}
|
}
|
||||||
return self.update_title();
|
tasks.push(self.update_title());
|
||||||
|
return Task::batch(tasks);
|
||||||
}
|
}
|
||||||
Message::TabNext => {
|
Message::TabNext => {
|
||||||
let len = self.tab_model.iter().count();
|
let len = self.tab_model.iter().count();
|
||||||
|
|
@ -3824,6 +3852,75 @@ impl Application for App {
|
||||||
self.update_tab(entity, tab_path, selection_paths),
|
self.update_tab(entity, tab_path, selection_paths),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
tab::Command::ContextMenu(point_opt) => {
|
||||||
|
#[cfg(feature = "wayland")]
|
||||||
|
match point_opt {
|
||||||
|
Some(point) => {
|
||||||
|
if crate::is_wayland() {
|
||||||
|
// Open context menu
|
||||||
|
use cctk::wayland_protocols::xdg::shell::client::xdg_positioner::{
|
||||||
|
Anchor, Gravity,
|
||||||
|
};
|
||||||
|
use cosmic::iced_runtime::platform_specific::wayland::popup::{
|
||||||
|
SctkPopupSettings, SctkPositioner,
|
||||||
|
};
|
||||||
|
let window_id = WindowId::unique();
|
||||||
|
self.windows.insert(
|
||||||
|
window_id.clone(),
|
||||||
|
WindowKind::ContextMenu(entity, widget::Id::unique()),
|
||||||
|
);
|
||||||
|
commands.push(self.update(Message::Surface(
|
||||||
|
cosmic::surface::action::app_popup(
|
||||||
|
move |app: &mut crate::App| -> SctkPopupSettings {
|
||||||
|
let anchor_rect = Rectangle {
|
||||||
|
x: point.x as i32,
|
||||||
|
y: point.y as i32,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
};
|
||||||
|
let positioner = SctkPositioner {
|
||||||
|
size: None,
|
||||||
|
anchor_rect,
|
||||||
|
anchor: Anchor::None,
|
||||||
|
gravity: Gravity::BottomRight,
|
||||||
|
reactive: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
SctkPopupSettings {
|
||||||
|
parent: app
|
||||||
|
.window_id_opt
|
||||||
|
.unwrap_or_else(|| WindowId::NONE),
|
||||||
|
id: window_id,
|
||||||
|
positioner,
|
||||||
|
parent_size: None,
|
||||||
|
grab: true,
|
||||||
|
close_with_children: false,
|
||||||
|
input_zone: None,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Destroy previous popup
|
||||||
|
let mut window_ids = Vec::new();
|
||||||
|
for (window_id, window_kind) in self.windows.iter() {
|
||||||
|
if let WindowKind::ContextMenu(e, _) = window_kind {
|
||||||
|
if *e == entity {
|
||||||
|
window_ids.push(*window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for window_id in window_ids {
|
||||||
|
commands.push(self.update(Message::Surface(
|
||||||
|
cosmic::surface::action::destroy_popup(window_id),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
tab::Command::Delete(paths) => commands.push(self.delete(paths)),
|
tab::Command::Delete(paths) => commands.push(self.delete(paths)),
|
||||||
tab::Command::DropFiles(to, from) => {
|
tab::Command::DropFiles(to, from) => {
|
||||||
commands.push(self.update(Message::PasteContents(to, from)));
|
commands.push(self.update(Message::PasteContents(to, from)));
|
||||||
|
|
@ -4055,10 +4152,12 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::WindowUnfocus => {
|
Message::WindowUnfocus => {
|
||||||
|
/*TODO
|
||||||
let tab_entity = self.tab_model.active();
|
let tab_entity = self.tab_model.active();
|
||||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(tab_entity) {
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(tab_entity) {
|
||||||
tab.context_menu = None;
|
tab.context_menu = None;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
Message::WindowCloseRequested(id) => {
|
Message::WindowCloseRequested(id) => {
|
||||||
self.remove_window(&id);
|
self.remove_window(&id);
|
||||||
|
|
@ -4548,9 +4647,9 @@ impl Application for App {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Surface(a) => {
|
Message::Surface(action) => {
|
||||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
cosmic::app::Action::Surface(a),
|
cosmic::app::Action::Surface(action),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Message::SaveSortNames => {
|
Message::SaveSortNames => {
|
||||||
|
|
@ -5583,6 +5682,19 @@ impl Application for App {
|
||||||
|
|
||||||
fn view_window(&self, id: WindowId) -> Element<Self::Message> {
|
fn view_window(&self, id: WindowId) -> Element<Self::Message> {
|
||||||
let content = match self.windows.get(&id) {
|
let content = match self.windows.get(&id) {
|
||||||
|
Some(WindowKind::ContextMenu(entity, id)) => {
|
||||||
|
match self.tab_model.data::<Tab>(*entity) {
|
||||||
|
Some(tab) => {
|
||||||
|
return widget::autosize::autosize(
|
||||||
|
menu::context_menu(tab, &self.key_binds, &self.modifiers)
|
||||||
|
.map(|x| Message::TabMessage(Some(*entity), x)),
|
||||||
|
id.clone(),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
None => widget::text("Unknown tab ID").into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(WindowKind::Desktop(entity)) => {
|
Some(WindowKind::Desktop(entity)) => {
|
||||||
let mut tab_column = widget::column::with_capacity(3);
|
let mut tab_column = widget::column::with_capacity(3);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -457,6 +457,7 @@ impl From<AppMessage> for Message {
|
||||||
AppMessage::ZoomIn(_entity_opt) => Message::ZoomIn,
|
AppMessage::ZoomIn(_entity_opt) => Message::ZoomIn,
|
||||||
AppMessage::ZoomOut(_entity_opt) => Message::ZoomOut,
|
AppMessage::ZoomOut(_entity_opt) => Message::ZoomOut,
|
||||||
AppMessage::NewItem(_entity_opt, true) => Message::NewFolder,
|
AppMessage::NewItem(_entity_opt, true) => Message::NewFolder,
|
||||||
|
AppMessage::Surface(action) => Message::Surface(action),
|
||||||
unsupported => {
|
unsupported => {
|
||||||
log::warn!("{unsupported:?} not supported in dialog mode");
|
log::warn!("{unsupported:?} not supported in dialog mode");
|
||||||
Message::None
|
Message::None
|
||||||
|
|
@ -1236,8 +1237,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tab.context_menu.is_some() {
|
if self.tab.context_menu.is_some() {
|
||||||
self.tab.context_menu = None;
|
return self.update(Message::TabMessage(tab::Message::ContextMenu(None)));
|
||||||
return Task::none();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tab.edit_location.is_some() {
|
if self.tab.edit_location.is_some() {
|
||||||
|
|
@ -1787,9 +1787,9 @@ impl Application for App {
|
||||||
tab::View::Grid => zoom_out(&mut config.icon_sizes.grid, 50, 500),
|
tab::View::Grid => zoom_out(&mut config.icon_sizes.grid, 50, 500),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Message::Surface(a) => {
|
Message::Surface(action) => {
|
||||||
return cosmic::task::message(cosmic::Action::Cosmic(
|
return cosmic::task::message(cosmic::Action::Cosmic(
|
||||||
cosmic::app::Action::Surface(a),
|
cosmic::app::Action::Surface(action),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,13 @@ pub fn home_dir() -> PathBuf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_wayland() -> bool {
|
||||||
|
matches!(
|
||||||
|
cosmic::app::cosmic::windowing_system(),
|
||||||
|
Some(cosmic::app::cosmic::WindowingSystem::Wayland)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs application in desktop mode
|
/// Runs application in desktop mode
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn desktop() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ pub struct MouseArea<'a, Message> {
|
||||||
on_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
||||||
on_resize: Option<Box<dyn OnResize<'a, Message>>>,
|
on_resize: Option<Box<dyn OnResize<'a, Message>>>,
|
||||||
on_right_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_right_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
||||||
on_right_press_no_capture: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_right_press_no_capture: bool,
|
||||||
|
on_right_press_window_position: bool,
|
||||||
on_right_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_right_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
||||||
on_middle_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_middle_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
||||||
on_middle_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
on_middle_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
|
||||||
|
|
@ -103,10 +104,17 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The message to emit on a right button press without capturing.
|
/// on_right_press will not capture input
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn on_right_press_no_capture(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
|
pub fn on_right_press_no_capture(mut self) -> Self {
|
||||||
self.on_right_press_no_capture = Some(Box::new(message));
|
self.on_right_press_no_capture = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// on_right_press will provide window position instead of widget relative
|
||||||
|
#[must_use]
|
||||||
|
pub fn on_right_press_window_position(mut self) -> Self {
|
||||||
|
self.on_right_press_window_position = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,8 +211,8 @@ impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option<Point>)
|
||||||
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
|
pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
|
impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
|
||||||
|
|
||||||
pub trait OnResize<'a, Message>: Fn(Size, Rectangle) -> Message + 'a {}
|
pub trait OnResize<'a, Message>: Fn(Rectangle) -> Message + 'a {}
|
||||||
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Size, Rectangle) -> Message + 'a {}
|
impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Rectangle) -> Message + 'a {}
|
||||||
|
|
||||||
pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta) -> Option<Message> + 'a {}
|
pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta) -> Option<Message> + 'a {}
|
||||||
impl<'a, Message, F> OnScroll<'a, Message> for F where
|
impl<'a, Message, F> OnScroll<'a, Message> for F where
|
||||||
|
|
@ -223,7 +231,7 @@ struct State {
|
||||||
last_virtual_position: Option<Point>,
|
last_virtual_position: Option<Point>,
|
||||||
drag_initiated: Option<Point>,
|
drag_initiated: Option<Point>,
|
||||||
prev_click: Option<(mouse::Click, Instant)>,
|
prev_click: Option<(mouse::Click, Instant)>,
|
||||||
size: Option<Size>,
|
viewport: Option<Rectangle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
|
|
@ -286,7 +294,8 @@ impl<'a, Message> MouseArea<'a, Message> {
|
||||||
on_release: None,
|
on_release: None,
|
||||||
on_resize: None,
|
on_resize: None,
|
||||||
on_right_press: None,
|
on_right_press: None,
|
||||||
on_right_press_no_capture: None,
|
on_right_press_no_capture: false,
|
||||||
|
on_right_press_window_position: false,
|
||||||
on_right_release: None,
|
on_right_release: None,
|
||||||
on_middle_press: None,
|
on_middle_press: None,
|
||||||
on_middle_release: None,
|
on_middle_release: None,
|
||||||
|
|
@ -507,10 +516,9 @@ fn update<Message: Clone>(
|
||||||
let layout_bounds = layout.bounds();
|
let layout_bounds = layout.bounds();
|
||||||
|
|
||||||
if let Some(message) = widget.on_resize.as_ref() {
|
if let Some(message) = widget.on_resize.as_ref() {
|
||||||
let size = layout_bounds.size();
|
if state.viewport != Some(*viewport) {
|
||||||
if state.size != Some(size) {
|
state.viewport = Some(*viewport);
|
||||||
state.size = Some(size);
|
shell.publish(message(*viewport));
|
||||||
shell.publish(message(size, *viewport));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,17 +643,18 @@ fn update<Message: Clone>(
|
||||||
|
|
||||||
if let Some(message) = widget.on_right_press.as_ref() {
|
if let Some(message) = widget.on_right_press.as_ref() {
|
||||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event {
|
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event {
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
let point_opt = if widget.on_right_press_window_position {
|
||||||
|
cursor.position_over(layout_bounds)
|
||||||
|
} else {
|
||||||
|
cursor.position_in(layout_bounds)
|
||||||
|
};
|
||||||
|
shell.publish(message(point_opt));
|
||||||
|
|
||||||
return event::Status::Captured;
|
if widget.on_right_press_no_capture {
|
||||||
}
|
return event::Status::Ignored;
|
||||||
}
|
} else {
|
||||||
|
return event::Status::Captured;
|
||||||
if let Some(message) = widget.on_right_press_no_capture.as_ref() {
|
}
|
||||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event {
|
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
|
||||||
|
|
||||||
return event::Status::Ignored;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
67
src/tab.rs
67
src/tab.rs
|
|
@ -1508,6 +1508,7 @@ pub enum Command {
|
||||||
AddToSidebar(PathBuf),
|
AddToSidebar(PathBuf),
|
||||||
AutoScroll(Option<f32>),
|
AutoScroll(Option<f32>),
|
||||||
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
ChangeLocation(String, Location, Option<Vec<PathBuf>>),
|
||||||
|
ContextMenu(Option<Point>),
|
||||||
Delete(Vec<PathBuf>),
|
Delete(Vec<PathBuf>),
|
||||||
DropFiles(PathBuf, ClipboardPaste),
|
DropFiles(PathBuf, ClipboardPaste),
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
|
|
@ -1566,9 +1567,9 @@ pub enum Message {
|
||||||
Reload,
|
Reload,
|
||||||
RightClick(Option<usize>),
|
RightClick(Option<usize>),
|
||||||
MiddleClick(usize),
|
MiddleClick(usize),
|
||||||
|
Resize(Rectangle),
|
||||||
Scroll(Viewport),
|
Scroll(Viewport),
|
||||||
ScrollTab(f32),
|
ScrollTab(f32),
|
||||||
ScrollToFocus,
|
|
||||||
SearchContext(Location, SearchContextWrapper),
|
SearchContext(Location, SearchContextWrapper),
|
||||||
SearchReady(bool),
|
SearchReady(bool),
|
||||||
SelectAll,
|
SelectAll,
|
||||||
|
|
@ -2384,6 +2385,7 @@ pub struct Tab {
|
||||||
pub location_context_menu_index: Option<usize>,
|
pub location_context_menu_index: Option<usize>,
|
||||||
pub context_menu: Option<Point>,
|
pub context_menu: Option<Point>,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
|
pub offset_opt: Option<Vector>,
|
||||||
pub scroll_opt: Option<AbsoluteOffset>,
|
pub scroll_opt: Option<AbsoluteOffset>,
|
||||||
pub size_opt: Cell<Option<Size>>,
|
pub size_opt: Cell<Option<Size>>,
|
||||||
pub item_view_size_opt: Cell<Option<Size>>,
|
pub item_view_size_opt: Cell<Option<Size>>,
|
||||||
|
|
@ -2495,6 +2497,7 @@ impl Tab {
|
||||||
location_context_menu_point: None,
|
location_context_menu_point: None,
|
||||||
location_context_menu_index: None,
|
location_context_menu_index: None,
|
||||||
mode: Mode::App,
|
mode: Mode::App,
|
||||||
|
offset_opt: None,
|
||||||
scroll_opt: None,
|
scroll_opt: None,
|
||||||
size_opt: Cell::new(None),
|
size_opt: Cell::new(None),
|
||||||
item_view_size_opt: Cell::new(None),
|
item_view_size_opt: Cell::new(None),
|
||||||
|
|
@ -2861,6 +2864,7 @@ impl Tab {
|
||||||
let mut history_i_opt = None;
|
let mut history_i_opt = None;
|
||||||
let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple();
|
let mod_ctrl = modifiers.contains(Modifiers::CTRL) && self.mode.multiple();
|
||||||
let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple();
|
let mod_shift = modifiers.contains(Modifiers::SHIFT) && self.mode.multiple();
|
||||||
|
let last_context_menu = self.context_menu;
|
||||||
match message {
|
match message {
|
||||||
Message::AddNetworkDrive => {
|
Message::AddNetworkDrive => {
|
||||||
commands.push(Command::AddNetworkDrive);
|
commands.push(Command::AddNetworkDrive);
|
||||||
|
|
@ -3087,6 +3091,7 @@ impl Tab {
|
||||||
self.edit_location = None;
|
self.edit_location = None;
|
||||||
if point_opt.is_none() || !mod_shift {
|
if point_opt.is_none() || !mod_shift {
|
||||||
self.context_menu = point_opt;
|
self.context_menu = point_opt;
|
||||||
|
|
||||||
//TODO: hack for clearing selecting when right clicking empty space
|
//TODO: hack for clearing selecting when right clicking empty space
|
||||||
if self.context_menu.is_some() && self.last_right_click.take().is_none() {
|
if self.context_menu.is_some() && self.last_right_click.take().is_none() {
|
||||||
if let Some(ref mut items) = self.items_opt {
|
if let Some(ref mut items) = self.items_opt {
|
||||||
|
|
@ -3578,7 +3583,16 @@ impl Tab {
|
||||||
item.highlighted = true;
|
item.highlighted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::Resize(viewport) => {
|
||||||
|
self.offset_opt = Some(Vector::new(viewport.x, viewport.y));
|
||||||
|
|
||||||
|
// Scroll to ensure focused item still in view
|
||||||
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
|
commands.push(Command::Iced(
|
||||||
|
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::Scroll(viewport) => {
|
Message::Scroll(viewport) => {
|
||||||
self.scroll_opt = Some(viewport.absolute_offset());
|
self.scroll_opt = Some(viewport.absolute_offset());
|
||||||
self.watch_drag = true;
|
self.watch_drag = true;
|
||||||
|
|
@ -3595,13 +3609,6 @@ impl Tab {
|
||||||
.into(),
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Message::ScrollToFocus => {
|
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
|
||||||
commands.push(Command::Iced(
|
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::SearchContext(location, context) => {
|
Message::SearchContext(location, context) => {
|
||||||
if location == self.location {
|
if location == self.location {
|
||||||
self.search_context = context.0;
|
self.search_context = context.0;
|
||||||
|
|
@ -3872,6 +3879,18 @@ impl Tab {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: check for wayland
|
||||||
|
if self.context_menu != last_context_menu {
|
||||||
|
if last_context_menu.is_some() {
|
||||||
|
commands.push(Command::ContextMenu(None));
|
||||||
|
}
|
||||||
|
if let Some(point) = self.context_menu {
|
||||||
|
commands.push(Command::ContextMenu(Some(
|
||||||
|
point + self.offset_opt.unwrap_or_default(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Change directory if requested
|
// Change directory if requested
|
||||||
if let Some(mut location) = cd {
|
if let Some(mut location) = cd {
|
||||||
if matches!(self.mode, Mode::Desktop) {
|
if matches!(self.mode, Mode::Desktop) {
|
||||||
|
|
@ -4480,9 +4499,9 @@ impl Tab {
|
||||||
Message::LocationContextMenuIndex(None)
|
Message::LocationContextMenuIndex(None)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
mouse_area = mouse_area.on_right_press_no_capture(move |_point_opt| {
|
mouse_area = mouse_area.on_right_press_no_capture().on_right_press(
|
||||||
Message::LocationContextMenuIndex(Some(index))
|
move |_point_opt| Message::LocationContextMenuIndex(Some(index)),
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let mouse_area = if let Location::Path(_) = &self.location {
|
let mouse_area = if let Location::Path(_) = &self.location {
|
||||||
|
|
@ -4739,9 +4758,9 @@ impl Tab {
|
||||||
column = column.push(button)
|
column = column.push(button)
|
||||||
} else {
|
} else {
|
||||||
column = column.push(
|
column = column.push(
|
||||||
mouse_area::MouseArea::new(button).on_right_press_no_capture(
|
mouse_area::MouseArea::new(button)
|
||||||
move |_point_opt| Message::RightClick(Some(i)),
|
.on_right_press_no_capture()
|
||||||
),
|
.on_right_press(move |_point_opt| Message::RightClick(Some(i))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5158,9 +5177,9 @@ impl Tab {
|
||||||
if self.context_menu.is_some() {
|
if self.context_menu.is_some() {
|
||||||
mouse_area
|
mouse_area
|
||||||
} else {
|
} else {
|
||||||
mouse_area.on_right_press_no_capture(move |_point_opt| {
|
mouse_area
|
||||||
Message::RightClick(Some(i))
|
.on_right_press_no_capture()
|
||||||
})
|
.on_right_press(move |_point_opt| Message::RightClick(Some(i)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -5375,8 +5394,7 @@ impl Tab {
|
||||||
let mut mouse_area = mouse_area::MouseArea::new(item_view)
|
let mut mouse_area = mouse_area::MouseArea::new(item_view)
|
||||||
.on_press(move |_point_opt| Message::Click(None))
|
.on_press(move |_point_opt| Message::Click(None))
|
||||||
.on_release(|_| Message::ClickRelease(None))
|
.on_release(|_| Message::ClickRelease(None))
|
||||||
//TODO: better way to keep focused item in view
|
.on_resize(Message::Resize)
|
||||||
.on_resize(|_, _| Message::ScrollToFocus)
|
|
||||||
.on_back_press(move |_point_opt| Message::GoPrevious)
|
.on_back_press(move |_point_opt| Message::GoPrevious)
|
||||||
.on_forward_press(move |_point_opt| Message::GoNext)
|
.on_forward_press(move |_point_opt| Message::GoNext)
|
||||||
.on_scroll(|delta| respond_to_scroll_direction(delta, self.modifiers));
|
.on_scroll(|delta| respond_to_scroll_direction(delta, self.modifiers));
|
||||||
|
|
@ -5388,12 +5406,13 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut popover = widget::popover(mouse_area);
|
let mut popover = widget::popover(mouse_area);
|
||||||
|
|
||||||
if let Some(point) = self.context_menu {
|
if let Some(point) = self.context_menu {
|
||||||
let context_menu = menu::context_menu(self, key_binds, &self.modifiers);
|
if !cfg!(feature = "wayland") || !crate::is_wayland() {
|
||||||
popover = popover
|
let context_menu = menu::context_menu(self, key_binds, &self.modifiers);
|
||||||
.popup(context_menu)
|
popover = popover
|
||||||
.position(widget::popover::Position::Point(point));
|
.popup(context_menu)
|
||||||
|
.position(widget::popover::Position::Point(point));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tab_column = widget::column::with_capacity(3);
|
let mut tab_column = widget::column::with_capacity(3);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue