feat: rebase libcosmic onto iced 0.14
This commit is contained in:
parent
03988df2dc
commit
360973175c
11 changed files with 2142 additions and 2014 deletions
2301
Cargo.lock
generated
2301
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -103,8 +103,8 @@ default = [
|
||||||
"io-uring",
|
"io-uring",
|
||||||
"lzma-rust2",
|
"lzma-rust2",
|
||||||
"notify",
|
"notify",
|
||||||
"wgpu",
|
|
||||||
"wayland",
|
"wayland",
|
||||||
|
"wgpu",
|
||||||
]
|
]
|
||||||
dbus-config = ["libcosmic/dbus-config"]
|
dbus-config = ["libcosmic/dbus-config"]
|
||||||
desktop = ["dep:cosmic-mime-apps", "dep:xdg"]
|
desktop = ["dep:cosmic-mime-apps", "dep:xdg"]
|
||||||
|
|
@ -146,6 +146,10 @@ tokio = { version = "1", features = ["rt", "macros"] }
|
||||||
# libcosmic = { path = "../libcosmic" }
|
# libcosmic = { path = "../libcosmic" }
|
||||||
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
# cosmic-config = { path = "../libcosmic/cosmic-config" }
|
||||||
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
# cosmic-theme = { path = "../libcosmic/cosmic-theme" }
|
||||||
|
# libcosmic = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||||
|
# cosmic-config = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||||
|
# cosmic-theme = { git = "https://github.com/pop-os/libcosmic//", branch = "iced-rebase" }
|
||||||
|
|
||||||
|
|
||||||
# [patch.'https://github.com/pop-os/smithay-clipboard']
|
# [patch.'https://github.com/pop-os/smithay-clipboard']
|
||||||
# smithay-clipboard = { path = "../smithay-clipboard" }
|
# smithay-clipboard = { path = "../smithay-clipboard" }
|
||||||
|
|
|
||||||
146
src/app.rs
146
src/app.rs
|
|
@ -33,16 +33,16 @@ use cosmic::{
|
||||||
window::{self, Event as WindowEvent, Id as WindowId},
|
window::{self, Event as WindowEvent, Id as WindowId},
|
||||||
},
|
},
|
||||||
iced_runtime::clipboard,
|
iced_runtime::clipboard,
|
||||||
iced_widget::button::focus,
|
iced_widget::{button::focus, scrollable::AbsoluteOffset},
|
||||||
style, surface, theme,
|
style, surface, theme,
|
||||||
widget::{
|
widget::{
|
||||||
self,
|
self,
|
||||||
about::About,
|
about::About,
|
||||||
dnd_destination::DragId,
|
dnd_destination::DragId,
|
||||||
horizontal_space, icon,
|
icon,
|
||||||
menu::{action::MenuAction, key_bind::KeyBind},
|
menu::{action::MenuAction, key_bind::KeyBind},
|
||||||
segmented_button::{self, Entity, ReorderEvent},
|
segmented_button::{self, Entity, ReorderEvent},
|
||||||
vertical_space,
|
space,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
|
|
@ -1900,7 +1900,7 @@ impl App {
|
||||||
section = section.add(widget::column::with_children([
|
section = section.add(widget::column::with_children([
|
||||||
widget::row::with_children([
|
widget::row::with_children([
|
||||||
widget::progress_bar(0.0..=1.0, progress)
|
widget::progress_bar(0.0..=1.0, progress)
|
||||||
.height(progress_bar_height)
|
.girth(progress_bar_height)
|
||||||
.into(),
|
.into(),
|
||||||
if controller.is_paused() {
|
if controller.is_paused() {
|
||||||
widget::tooltip(
|
widget::tooltip(
|
||||||
|
|
@ -2400,8 +2400,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Element::from(
|
Some(Element::from(
|
||||||
// XXX both must be shrink to avoid flex layout from ignoring it
|
nav.width(Length::Shrink).height(Length::Fill),
|
||||||
nav.width(Length::Shrink).height(Length::Shrink),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3213,7 +3212,10 @@ impl Application for App {
|
||||||
if let Some(offset) = tab.select_focus_scroll() {
|
if let Some(offset) = tab.select_focus_scroll() {
|
||||||
return scrollable::scroll_to(
|
return scrollable::scroll_to(
|
||||||
tab.scrollable_id.clone(),
|
tab.scrollable_id.clone(),
|
||||||
offset,
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4199,7 +4201,13 @@ impl Application for App {
|
||||||
//Restore scroll
|
//Restore scroll
|
||||||
//TODO: why do scrollers with different IDs get the same scroll position?
|
//TODO: why do scrollers with different IDs get the same scroll position?
|
||||||
let scroll = tab.scroll_opt.unwrap_or_default();
|
let scroll = tab.scroll_opt.unwrap_or_default();
|
||||||
tasks.push(scrollable::scroll_to(tab.scrollable_id.clone(), scroll));
|
tasks.push(scrollable::scroll_to(
|
||||||
|
tab.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(scroll.x),
|
||||||
|
y: Some(scroll.y),
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
self.activate_nav_model_location(&tab.location.clone());
|
self.activate_nav_model_location(&tab.location.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -5229,7 +5237,7 @@ impl Application for App {
|
||||||
.title(fl!("add-network-drive"))
|
.title(fl!("add-network-drive"))
|
||||||
.header(text_input)
|
.header(text_input)
|
||||||
.footer(widget::row::with_children([
|
.footer(widget::row::with_children([
|
||||||
widget::horizontal_space().into(),
|
widget::space::horizontal().into(),
|
||||||
button.into(),
|
button.into(),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
@ -5251,7 +5259,7 @@ impl Application for App {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| widget::horizontal_space().into());
|
.unwrap_or_else(|| widget::space::horizontal().into());
|
||||||
context_drawer::context_drawer(
|
context_drawer::context_drawer(
|
||||||
self.preview(entity_opt, kind, true)
|
self.preview(entity_opt, kind, true)
|
||||||
.map(move |x| Message::TabMessage(Some(entity), x)),
|
.map(move |x| Message::TabMessage(Some(entity), x)),
|
||||||
|
|
@ -5534,8 +5542,9 @@ impl Application for App {
|
||||||
//TODO: what should submit do?
|
//TODO: what should submit do?
|
||||||
//TODO: button for showing password
|
//TODO: button for showing password
|
||||||
controls = controls.push(
|
controls = controls.push(
|
||||||
widget::checkbox(fl!("remember-password"), *remember).on_toggle(
|
widget::checkbox(*remember)
|
||||||
move |value| {
|
.label(fl!("remember-password"))
|
||||||
|
.on_toggle(move |value| {
|
||||||
Message::DialogUpdate(DialogPage::NetworkAuth {
|
Message::DialogUpdate(DialogPage::NetworkAuth {
|
||||||
mounter_key: *mounter_key,
|
mounter_key: *mounter_key,
|
||||||
uri: uri.clone(),
|
uri: uri.clone(),
|
||||||
|
|
@ -5545,8 +5554,7 @@ impl Application for App {
|
||||||
},
|
},
|
||||||
auth_tx: auth_tx.clone(),
|
auth_tx: auth_tx.clone(),
|
||||||
})
|
})
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5710,11 +5718,13 @@ impl Application for App {
|
||||||
} else {
|
} else {
|
||||||
widget::text::body(app.name.clone()).into()
|
widget::text::body(app.name.clone()).into()
|
||||||
},
|
},
|
||||||
widget::horizontal_space().into(),
|
widget::space::horizontal().into(),
|
||||||
if *selected == i {
|
if *selected == i {
|
||||||
icon::from_name("checkbox-checked-symbolic").size(16).into()
|
icon::from_name("checkbox-checked-symbolic").size(16).into()
|
||||||
} else {
|
} else {
|
||||||
widget::Space::with_width(Length::Fixed(16.0)).into()
|
widget::space::horizontal()
|
||||||
|
.width(Length::Fixed(16.0))
|
||||||
|
.into()
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.spacing(space_s)
|
.spacing(space_s)
|
||||||
|
|
@ -5919,10 +5929,8 @@ impl Application for App {
|
||||||
if *multiple {
|
if *multiple {
|
||||||
dialog
|
dialog
|
||||||
.control(
|
.control(
|
||||||
widget::checkbox(
|
widget::checkbox(*apply_to_all)
|
||||||
format!("{} ({})", fl!("apply-to-all"), *conflict_count),
|
.label(format!("{} ({})", fl!("apply-to-all"), *conflict_count))
|
||||||
*apply_to_all,
|
|
||||||
)
|
|
||||||
.on_toggle(|apply_to_all| {
|
.on_toggle(|apply_to_all| {
|
||||||
Message::DialogUpdate(DialogPage::Replace {
|
Message::DialogUpdate(DialogPage::Replace {
|
||||||
from: from.clone(),
|
from: from.clone(),
|
||||||
|
|
@ -6053,7 +6061,7 @@ impl Application for App {
|
||||||
//TODO: get height from theme?
|
//TODO: get height from theme?
|
||||||
let progress_bar_height = Length::Fixed(4.0);
|
let progress_bar_height = Length::Fixed(4.0);
|
||||||
let progress_bar =
|
let progress_bar =
|
||||||
widget::progress_bar(0.0..=1.0, total_progress).height(progress_bar_height);
|
widget::progress_bar(0.0..=1.0, total_progress).girth(progress_bar_height);
|
||||||
|
|
||||||
let container = widget::layer_container(widget::column::with_children([
|
let container = widget::layer_container(widget::column::with_children([
|
||||||
widget::row::with_children([
|
widget::row::with_children([
|
||||||
|
|
@ -6089,14 +6097,14 @@ impl Application for App {
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.into(),
|
.into(),
|
||||||
widget::text::body(title).into(),
|
widget::text::body(title).into(),
|
||||||
widget::Space::with_height(space_s).into(),
|
widget::space::vertical().height(space_s).into(),
|
||||||
widget::row::with_children([
|
widget::row::with_children([
|
||||||
widget::button::link(fl!("details"))
|
widget::button::link(fl!("details"))
|
||||||
.on_press(Message::ToggleContextPage(ContextPage::EditHistory))
|
.on_press(Message::ToggleContextPage(ContextPage::EditHistory))
|
||||||
.padding(0)
|
.padding(0)
|
||||||
.trailing_icon(true)
|
.trailing_icon(true)
|
||||||
.into(),
|
.into(),
|
||||||
widget::horizontal_space().into(),
|
widget::space::horizontal().into(),
|
||||||
widget::button::standard(fl!("dismiss"))
|
widget::button::standard(fl!("dismiss"))
|
||||||
.on_press(Message::PendingDismiss)
|
.on_press(Message::PendingDismiss)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -6218,7 +6226,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The toaster is added on top of an empty element to ensure that it does not override context menus
|
// The toaster is added on top of an empty element to ensure that it does not override context menus
|
||||||
tab_column = tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space()));
|
tab_column = tab_column.push(widget::toaster(&self.toasts, widget::space::horizontal()));
|
||||||
|
|
||||||
let content: Element<_> = tab_column.into();
|
let content: Element<_> = tab_column.into();
|
||||||
|
|
||||||
|
|
@ -6257,27 +6265,27 @@ impl Application for App {
|
||||||
self.clipboard_has_content(),
|
self.clipboard_has_content(),
|
||||||
)
|
)
|
||||||
.map(move |message| Message::TabMessage(Some(*entity), message)),
|
.map(move |message| Message::TabMessage(Some(*entity), message)),
|
||||||
None => widget::vertical_space().into(),
|
None => widget::space::vertical().into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
tab_column = tab_column.push(tab_view);
|
tab_column = tab_column.push(tab_view);
|
||||||
|
|
||||||
// The toaster is added on top of an empty element to ensure that it does not override context menus
|
// The toaster is added on top of an empty element to ensure that it does not override context menus
|
||||||
tab_column =
|
tab_column =
|
||||||
tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space()));
|
tab_column.push(widget::toaster(&self.toasts, widget::space::horizontal()));
|
||||||
return if let Some(margin) = self.margin.get(&id) {
|
return if let Some(margin) = self.margin.get(&id) {
|
||||||
if margin.0 >= 0. || margin.2 >= 0. {
|
if margin.0 >= 0. || margin.2 >= 0. {
|
||||||
tab_column = widget::column::with_children([
|
tab_column = widget::column::with_children([
|
||||||
vertical_space().height(margin.0).into(),
|
space::vertical().height(margin.0).into(),
|
||||||
tab_column.into(),
|
tab_column.into(),
|
||||||
vertical_space().height(margin.2).into(),
|
space::vertical().height(margin.2).into(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if margin.1 >= 0. || margin.3 >= 0. {
|
if margin.1 >= 0. || margin.3 >= 0. {
|
||||||
Element::from(widget::row::with_children([
|
Element::from(widget::row::with_children([
|
||||||
horizontal_space().width(margin.1).into(),
|
space::horizontal().width(margin.1).into(),
|
||||||
tab_column.into(),
|
tab_column.into(),
|
||||||
horizontal_space().width(margin.3).into(),
|
space::horizontal().width(margin.3).into(),
|
||||||
]))
|
]))
|
||||||
} else {
|
} else {
|
||||||
tab_column.into()
|
tab_column.into()
|
||||||
|
|
@ -6289,7 +6297,7 @@ impl Application for App {
|
||||||
WindowKind::DesktopViewOptions => self.desktop_view_options(),
|
WindowKind::DesktopViewOptions => self.desktop_view_options(),
|
||||||
WindowKind::Dialogs(id) => match self.dialog() {
|
WindowKind::Dialogs(id) => match self.dialog() {
|
||||||
Some(element) => return widget::autosize::autosize(element, id.clone()).into(),
|
Some(element) => return widget::autosize::autosize(element, id.clone()).into(),
|
||||||
None => widget::horizontal_space().into(),
|
None => widget::space::horizontal().into(),
|
||||||
},
|
},
|
||||||
WindowKind::Preview(entity_opt, kind) => self
|
WindowKind::Preview(entity_opt, kind) => self
|
||||||
.preview(entity_opt, kind, false)
|
.preview(entity_opt, kind, false)
|
||||||
|
|
@ -6309,7 +6317,10 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
widget::container(widget::scrollable(content))
|
widget::container(widget::id_container(
|
||||||
|
widget::scrollable(content),
|
||||||
|
widget::Id::new("main container for files"),
|
||||||
|
))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.class(theme::Container::WindowBackground)
|
.class(theme::Container::WindowBackground)
|
||||||
|
|
@ -6403,9 +6414,10 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
Message::TimeConfigChange(update.config)
|
Message::TimeConfigChange(update.config)
|
||||||
}),
|
}),
|
||||||
Subscription::run_with_id(
|
Subscription::run_with(TypeId::of::<WatcherSubscription>(), |_| {
|
||||||
TypeId::of::<WatcherSubscription>(),
|
stream::channel(
|
||||||
stream::channel(100, |mut output| async move {
|
100,
|
||||||
|
|mut output: futures::channel::mpsc::Sender<Message>| async move {
|
||||||
let watcher_res = {
|
let watcher_res = {
|
||||||
let mut output = output.clone();
|
let mut output = output.clone();
|
||||||
new_debouncer(
|
new_debouncer(
|
||||||
|
|
@ -6476,16 +6488,18 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
|
},
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
),
|
Subscription::run_with(TypeId::of::<TrashWatcherSubscription>(), |_| {
|
||||||
Subscription::run_with_id(
|
stream::channel(
|
||||||
TypeId::of::<TrashWatcherSubscription>(),
|
1,
|
||||||
stream::channel(1, |mut output| async move {
|
|mut output: futures::channel::mpsc::Sender<Message>| async move {
|
||||||
let watcher_res = new_debouncer(
|
let watcher_res = new_debouncer(
|
||||||
time::Duration::from_millis(250),
|
time::Duration::from_millis(250),
|
||||||
Some(time::Duration::from_millis(250)),
|
Some(time::Duration::from_millis(250)),
|
||||||
move |event_res: notify_debouncer_full::DebounceEventResult| match event_res
|
move |event_res: notify_debouncer_full::DebounceEventResult| {
|
||||||
{
|
match event_res {
|
||||||
Ok(events) => {
|
Ok(events) => {
|
||||||
// Rescan on any event. We don't need to evaluate each event
|
// Rescan on any event. We don't need to evaluate each event
|
||||||
// because as long as the trash changed in any way we need to
|
// because as long as the trash changed in any way we need to
|
||||||
|
|
@ -6506,6 +6520,7 @@ impl Application for App {
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to watch trash bin for changes: {e:?}");
|
log::warn!("failed to watch trash bin for changes: {e:?}");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -6547,19 +6562,18 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
|
},
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
not(feature = "desktop-applet"),
|
not(feature = "desktop-applet"),
|
||||||
not(target_os = "ios"),
|
not(target_os = "ios"),
|
||||||
not(target_os = "android")
|
not(target_os = "android")
|
||||||
))]
|
))]
|
||||||
if self.config.show_recents {
|
Subscription::run_with(TypeId::of::<RecentsWatcherSubscription>(), |_| {
|
||||||
subscriptions.push(Subscription::run_with_id(
|
stream::channel(
|
||||||
TypeId::of::<RecentsWatcherSubscription>(),
|
1,
|
||||||
stream::channel(1, |mut output| async move {
|
|mut output: futures::channel::mpsc::Sender<Message>| async move {
|
||||||
let Some(recents_path) = recently_used_xbel::dir() else {
|
let Some(recents_path) = recently_used_xbel::dir() else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"failed to watch recents changes: .recently_used.xbel does not exist"
|
"failed to watch recents changes: .recently_used.xbel does not exist"
|
||||||
|
|
@ -6570,8 +6584,8 @@ impl Application for App {
|
||||||
let watcher_res = new_debouncer(
|
let watcher_res = new_debouncer(
|
||||||
time::Duration::from_millis(250),
|
time::Duration::from_millis(250),
|
||||||
Some(time::Duration::from_millis(250)),
|
Some(time::Duration::from_millis(250)),
|
||||||
move |event_res: notify_debouncer_full::DebounceEventResult| match event_res
|
move |event_res: notify_debouncer_full::DebounceEventResult| {
|
||||||
{
|
match event_res {
|
||||||
Ok(events) => {
|
Ok(events) => {
|
||||||
// Programs differ in how they modify the recents file so the
|
// Programs differ in how they modify the recents file so the
|
||||||
// rescan is triggered on any event but access.
|
// rescan is triggered on any event but access.
|
||||||
|
|
@ -6590,15 +6604,18 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to watch recents file for changes: {e:?}")
|
log::warn!(
|
||||||
|
"failed to watch recents file for changes: {e:?}"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
match watcher_res {
|
match watcher_res {
|
||||||
Ok(mut watcher) => {
|
Ok(mut watcher) => {
|
||||||
if let Err(e) =
|
if let Err(e) = watcher
|
||||||
watcher.watch(&recents_path, notify::RecursiveMode::NonRecursive)
|
.watch(&recents_path, notify::RecursiveMode::NonRecursive)
|
||||||
{
|
{
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"failed to add recents file `{}` to watcher: {}",
|
"failed to add recents file `{}` to watcher: {}",
|
||||||
|
|
@ -6616,9 +6633,10 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
|
},
|
||||||
|
)
|
||||||
}),
|
}),
|
||||||
));
|
];
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scroll_speed) = self.auto_scroll_speed {
|
if let Some(scroll_speed) = self.auto_scroll_speed {
|
||||||
subscriptions.push(
|
subscriptions.push(
|
||||||
|
|
@ -6663,10 +6681,14 @@ impl Application for App {
|
||||||
// Handle notification when window is closed and operations are in progress
|
// Handle notification when window is closed and operations are in progress
|
||||||
#[cfg(feature = "notify")]
|
#[cfg(feature = "notify")]
|
||||||
{
|
{
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
struct NotificationSubscription;
|
struct NotificationSubscription;
|
||||||
subscriptions.push(Subscription::run_with_id(
|
subscriptions.push(Subscription::run_with(
|
||||||
TypeId::of::<NotificationSubscription>(),
|
TypeId::of::<NotificationSubscription>(),
|
||||||
stream::channel(1, move |msg_tx| async move {
|
|_| {
|
||||||
|
stream::channel(
|
||||||
|
1,
|
||||||
|
move |msg_tx: futures::channel::mpsc::Sender<_>| async move {
|
||||||
let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx));
|
let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx));
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
match notify_rust::Notification::new()
|
match notify_rust::Notification::new()
|
||||||
|
|
@ -6679,9 +6701,9 @@ impl Application for App {
|
||||||
msg_tx
|
msg_tx
|
||||||
.lock()
|
.lock()
|
||||||
.await
|
.await
|
||||||
.send(Message::Notification(Arc::new(Mutex::new(
|
.send(Message::Notification(Arc::new(
|
||||||
notification,
|
Mutex::new(notification),
|
||||||
))))
|
)))
|
||||||
.await
|
.await
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -6694,7 +6716,9 @@ impl Application for App {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
}),
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ use cosmic::{
|
||||||
window,
|
window,
|
||||||
},
|
},
|
||||||
iced_core::widget::operation,
|
iced_core::widget::operation,
|
||||||
|
iced_widget::scrollable::AbsoluteOffset,
|
||||||
iced_winit::{self, SurfaceIdWrapper},
|
iced_winit::{self, SurfaceIdWrapper},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
|
|
@ -202,7 +203,8 @@ impl<T: AsRef<str>> From<T> for DialogLabel {
|
||||||
|
|
||||||
impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> {
|
impl<'a, M: Clone + 'static> From<&'a DialogLabel> for Element<'a, M> {
|
||||||
fn from(label: &'a DialogLabel) -> Self {
|
fn from(label: &'a DialogLabel) -> Self {
|
||||||
let mut iced_spans = Vec::with_capacity(label.spans.len());
|
let mut iced_spans: Vec<cosmic::iced_core::text::Span<'_, ()>> =
|
||||||
|
Vec::with_capacity(label.spans.len());
|
||||||
for span in &label.spans {
|
for span in &label.spans {
|
||||||
iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline));
|
iced_spans.push(cosmic::iced::widget::span(&span.text).underline(span.underline));
|
||||||
}
|
}
|
||||||
|
|
@ -615,10 +617,13 @@ impl App {
|
||||||
for (choice_i, choice) in self.choices.iter().enumerate() {
|
for (choice_i, choice) in self.choices.iter().enumerate() {
|
||||||
match choice {
|
match choice {
|
||||||
DialogChoice::CheckBox { label, value, .. } => {
|
DialogChoice::CheckBox { label, value, .. } => {
|
||||||
row =
|
row = row.push(
|
||||||
row.push(widget::checkbox(label, *value).on_toggle(move |checked| {
|
widget::checkbox(*value)
|
||||||
|
.label(label)
|
||||||
|
.on_toggle(move |checked| {
|
||||||
Message::Choice(choice_i, usize::from(checked))
|
Message::Choice(choice_i, usize::from(checked))
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
DialogChoice::ComboBox {
|
DialogChoice::ComboBox {
|
||||||
label,
|
label,
|
||||||
|
|
@ -640,7 +645,7 @@ impl App {
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.spacing(space_xxs);
|
.spacing(space_xxs);
|
||||||
}
|
}
|
||||||
row = row.push(widget::horizontal_space());
|
row = row.push(widget::space::horizontal());
|
||||||
row = row.push(widget::button::standard(fl!("cancel")).on_press(Message::Cancel));
|
row = row.push(widget::button::standard(fl!("cancel")).on_press(Message::Cancel));
|
||||||
|
|
||||||
let mut has_selected = false;
|
let mut has_selected = false;
|
||||||
|
|
@ -1103,7 +1108,7 @@ impl Application for App {
|
||||||
.find(|item| item.selected)
|
.find(|item| item.selected)
|
||||||
.map(|item| item.preview_actions().map(Message::TabMessage))
|
.map(|item| item.preview_actions().map(Message::TabMessage))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| widget::horizontal_space().into());
|
.unwrap_or_else(|| widget::space::horizontal().into());
|
||||||
Some(
|
Some(
|
||||||
context_drawer::context_drawer(
|
context_drawer::context_drawer(
|
||||||
self.preview(kind).map(Message::TabMessage),
|
self.preview(kind).map(Message::TabMessage),
|
||||||
|
|
@ -1289,8 +1294,7 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(Element::from(
|
Some(Element::from(
|
||||||
// XXX both must be shrink to avoid flex layout from ignoring it
|
nav.width(Length::Shrink).height(Length::Fill),
|
||||||
nav.width(Length::Shrink).height(Length::Shrink),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1506,7 +1510,10 @@ impl Application for App {
|
||||||
if let Some(offset) = self.tab.select_focus_scroll() {
|
if let Some(offset) = self.tab.select_focus_scroll() {
|
||||||
return scrollable::scroll_to(
|
return scrollable::scroll_to(
|
||||||
self.tab.scrollable_id.clone(),
|
self.tab.scrollable_id.clone(),
|
||||||
offset,
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2076,9 +2083,9 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
Message::TimeConfigChange(update.config)
|
Message::TimeConfigChange(update.config)
|
||||||
}),
|
}),
|
||||||
Subscription::run_with_id(
|
Subscription::run_with(TypeId::of::<WatcherSubscription>(), |_| {
|
||||||
TypeId::of::<WatcherSubscription>(),
|
stream::channel(100, {
|
||||||
stream::channel(100, |mut output| async move {
|
|mut output: futures::channel::mpsc::Sender<_>| async move {
|
||||||
let watcher_res = {
|
let watcher_res = {
|
||||||
let mut output = output.clone();
|
let mut output = output.clone();
|
||||||
new_debouncer(
|
new_debouncer(
|
||||||
|
|
@ -2147,8 +2154,9 @@ impl Application for App {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
|
}
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
),
|
|
||||||
self.tab
|
self.tab
|
||||||
.subscription(
|
.subscription(
|
||||||
self.core.window.show_context
|
self.core.window.show_context
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common keys
|
// Common keys
|
||||||
bind!([], Key::Named(Named::Space), Gallery);
|
|
||||||
bind!([], Key::Named(Named::ArrowDown), ItemDown);
|
bind!([], Key::Named(Named::ArrowDown), ItemDown);
|
||||||
bind!([], Key::Named(Named::ArrowLeft), ItemLeft);
|
bind!([], Key::Named(Named::ArrowLeft), ItemLeft);
|
||||||
bind!([], Key::Named(Named::ArrowRight), ItemRight);
|
bind!([], Key::Named(Named::ArrowRight), ItemRight);
|
||||||
|
|
@ -40,7 +39,9 @@ pub fn key_binds(mode: &tab::Mode) -> HashMap<KeyBind, Action> {
|
||||||
bind!([Shift], Key::Named(Named::End), SelectLast);
|
bind!([Shift], Key::Named(Named::End), SelectLast);
|
||||||
bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder);
|
bind!([Ctrl, Shift], Key::Character("n".into()), NewFolder);
|
||||||
bind!([], Key::Named(Named::Enter), Open);
|
bind!([], Key::Named(Named::Enter), Open);
|
||||||
bind!([Ctrl], Key::Named(Named::Space), Preview);
|
bind!([Ctrl], Key::Character(" ".into()), Preview);
|
||||||
|
bind!([], Key::Character(" ".into()), Gallery);
|
||||||
|
|
||||||
bind!([Ctrl], Key::Character("h".into()), ToggleShowHidden);
|
bind!([Ctrl], Key::Character("h".into()), ToggleShowHidden);
|
||||||
bind!([Ctrl], Key::Character("a".into()), SelectAll);
|
bind!([Ctrl], Key::Character("a".into()), SelectAll);
|
||||||
bind!([Ctrl], Key::Character("=".into()), ZoomIn);
|
bind!([Ctrl], Key::Character("=".into()), ZoomIn);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub mod config;
|
||||||
pub mod dialog;
|
pub mod dialog;
|
||||||
mod key_bind;
|
mod key_bind;
|
||||||
pub(crate) mod large_image;
|
pub(crate) mod large_image;
|
||||||
|
pub(crate) mod load_image;
|
||||||
mod localize;
|
mod localize;
|
||||||
mod menu;
|
mod menu;
|
||||||
mod mime_app;
|
mod mime_app;
|
||||||
|
|
|
||||||
219
src/load_image.rs
Normal file
219
src/load_image.rs
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
use cosmic::{iced_core, iced_widget};
|
||||||
|
use iced_core::event::Event;
|
||||||
|
use iced_core::layout;
|
||||||
|
use iced_core::mouse;
|
||||||
|
use iced_core::overlay;
|
||||||
|
use iced_core::renderer;
|
||||||
|
use iced_core::widget::{Operation, Tree};
|
||||||
|
use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget};
|
||||||
|
|
||||||
|
pub fn loaded_image<'a, Message: 'static, Theme>(
|
||||||
|
handle: <cosmic::Renderer as iced_core::image::Renderer>::Handle,
|
||||||
|
) -> LoadedImage<'a, Message, Theme, cosmic::Renderer>
|
||||||
|
where
|
||||||
|
Theme: iced_widget::container::Catalog,
|
||||||
|
<Theme as iced_widget::container::Catalog>::Class<'a>: From<cosmic::theme::Container<'a>>,
|
||||||
|
{
|
||||||
|
LoadedImage::new(handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forces the wrapped image to be loaded before drawing.
|
||||||
|
///
|
||||||
|
/// May cause a dropped frame if the image is not already in the cache.
|
||||||
|
/// This is useful when you want to ensure an image is loaded before it is drawn, for example when swapping out a placeholder.
|
||||||
|
/// Otherwise, the image may be blank until the next redraw.
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct LoadedImage<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced_core::Renderer + iced_core::image::Renderer,
|
||||||
|
{
|
||||||
|
handle: <Renderer as iced_core::image::Renderer>::Handle,
|
||||||
|
content: cosmic::iced::Element<'a, Message, Theme, Renderer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> LoadedImage<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced_core::Renderer + iced_core::image::Renderer,
|
||||||
|
<Renderer as iced_core::image::Renderer>::Handle: 'a,
|
||||||
|
{
|
||||||
|
/// Creates an empty [`LoadedImage`].
|
||||||
|
pub(crate) fn new(handle: <Renderer as iced_core::image::Renderer>::Handle) -> Self {
|
||||||
|
LoadedImage {
|
||||||
|
handle: handle.clone(),
|
||||||
|
content: cosmic::widget::Image::new(handle).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
for LoadedImage<'_, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: iced_core::Renderer + iced_core::image::Renderer,
|
||||||
|
{
|
||||||
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
vec![Tree::new(&self.content)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
|
tree.diff_children(std::slice::from_mut(&mut self.content));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> iced_core::Size<Length> {
|
||||||
|
self.content.as_widget().size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
let node = self
|
||||||
|
.content
|
||||||
|
.as_widget_mut()
|
||||||
|
.layout(&mut tree.children[0], renderer, limits);
|
||||||
|
let size = node.size();
|
||||||
|
layout::Node::with_children(size, vec![node])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
operation: &mut dyn Operation,
|
||||||
|
) {
|
||||||
|
operation.container(None, layout.bounds());
|
||||||
|
operation.traverse(&mut |operation| {
|
||||||
|
self.content.as_widget_mut().operate(
|
||||||
|
&mut tree.children[0],
|
||||||
|
layout
|
||||||
|
.children()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
renderer,
|
||||||
|
operation,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
event: &Event,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: mouse::Cursor,
|
||||||
|
renderer: &Renderer,
|
||||||
|
clipboard: &mut dyn Clipboard,
|
||||||
|
shell: &mut Shell<'_, Message>,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
self.content.as_widget_mut().update(
|
||||||
|
&mut tree.children[0],
|
||||||
|
event,
|
||||||
|
layout
|
||||||
|
.children()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
cursor_position,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
|
viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_interaction(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> mouse::Interaction {
|
||||||
|
let content_layout = layout.children().next().unwrap();
|
||||||
|
self.content.as_widget().mouse_interaction(
|
||||||
|
&tree.children[0],
|
||||||
|
content_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
cursor_position,
|
||||||
|
viewport,
|
||||||
|
renderer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
theme: &Theme,
|
||||||
|
renderer_style: &renderer::Style,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor_position: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
let content_layout = layout.children().next().unwrap();
|
||||||
|
|
||||||
|
// forces image to be loaded before drawing
|
||||||
|
_ = renderer.load_image(&self.handle);
|
||||||
|
self.content.as_widget().draw(
|
||||||
|
&tree.children[0],
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
renderer_style,
|
||||||
|
content_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
cursor_position,
|
||||||
|
viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlay<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
tree: &'b mut Tree,
|
||||||
|
layout: Layout<'b>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
translation: Vector,
|
||||||
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
|
self.content.as_widget_mut().overlay(
|
||||||
|
&mut tree.children[0],
|
||||||
|
layout
|
||||||
|
.children()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
renderer,
|
||||||
|
viewport,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drag_destinations(
|
||||||
|
&self,
|
||||||
|
state: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
|
||||||
|
) {
|
||||||
|
let content_layout = layout.children().next().unwrap();
|
||||||
|
self.content.as_widget().drag_destinations(
|
||||||
|
&state.children[0],
|
||||||
|
content_layout.with_virtual_offset(layout.virtual_offset()),
|
||||||
|
renderer,
|
||||||
|
dnd_rectangles,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> From<LoadedImage<'a, Message, Theme, Renderer>>
|
||||||
|
for Element<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Message: 'a,
|
||||||
|
Renderer: 'a + iced_core::Renderer + iced_core::image::Renderer,
|
||||||
|
Theme: 'a,
|
||||||
|
{
|
||||||
|
fn from(c: LoadedImage<'a, Message, Theme, Renderer>) -> Element<'a, Message, Theme, Renderer> {
|
||||||
|
Element::new(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,9 @@ use cosmic::{
|
||||||
},
|
},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self, Row, button, column, container, divider, horizontal_space,
|
self, Row, button, column, container, divider,
|
||||||
menu::{self, ItemHeight, ItemWidth, MenuBar, key_bind::KeyBind},
|
menu::{self, ItemHeight, ItemWidth, MenuBar, key_bind::KeyBind},
|
||||||
responsive_menu_bar, text,
|
responsive_menu_bar, space, text,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use i18n_embed::LanguageLoader;
|
use i18n_embed::LanguageLoader;
|
||||||
|
|
@ -88,7 +88,7 @@ pub fn context_menu<'a>(
|
||||||
let key = find_key(&action);
|
let key = find_key(&action);
|
||||||
menu_button!(
|
menu_button!(
|
||||||
text::body(label),
|
text::body(label),
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
text::body(key).class(theme::Text::Custom(key_style))
|
text::body(key).class(theme::Text::Custom(key_style))
|
||||||
)
|
)
|
||||||
.on_press(tab::Message::ContextAction(action))
|
.on_press(tab::Message::ContextAction(action))
|
||||||
|
|
@ -98,7 +98,7 @@ pub fn context_menu<'a>(
|
||||||
let key = find_key(&action);
|
let key = find_key(&action);
|
||||||
menu_button!(
|
menu_button!(
|
||||||
text::body(label).class(theme::Text::Custom(disabled_style)),
|
text::body(label).class(theme::Text::Custom(disabled_style)),
|
||||||
horizontal_space(),
|
space::horizontal(),
|
||||||
text::body(key).class(theme::Text::Custom(disabled_style))
|
text::body(key).class(theme::Text::Custom(disabled_style))
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use cosmic::{
|
||||||
widget,
|
widget,
|
||||||
};
|
};
|
||||||
use gio::{glib, prelude::*};
|
use gio::{glib, prelude::*};
|
||||||
use std::{any::TypeId, cell::Cell, future::pending, path::PathBuf, sync::Arc};
|
use std::{any::TypeId, cell::Cell, future::pending, hash::Hash, path::PathBuf, sync::Arc};
|
||||||
use tokio::sync::{Mutex, mpsc};
|
use tokio::sync::{Mutex, mpsc};
|
||||||
|
|
||||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||||
|
|
@ -668,9 +668,31 @@ impl Mounter for Gvfs {
|
||||||
fn subscription(&self) -> Subscription<MounterMessage> {
|
fn subscription(&self) -> Subscription<MounterMessage> {
|
||||||
let command_tx = self.command_tx.clone();
|
let command_tx = self.command_tx.clone();
|
||||||
let event_rx = self.event_rx.clone();
|
let event_rx = self.event_rx.clone();
|
||||||
Subscription::run_with_id(
|
struct Wrapper {
|
||||||
TypeId::of::<Self>(),
|
command_tx: mpsc::UnboundedSender<Cmd>,
|
||||||
stream::channel(1, |mut output| async move {
|
event_rx: Arc<Mutex<mpsc::UnboundedReceiver<Event>>>,
|
||||||
|
}
|
||||||
|
impl Hash for Wrapper {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
TypeId::of::<Self>().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Subscription::run_with(
|
||||||
|
Wrapper {
|
||||||
|
command_tx,
|
||||||
|
event_rx,
|
||||||
|
},
|
||||||
|
|Wrapper {
|
||||||
|
command_tx,
|
||||||
|
event_rx,
|
||||||
|
}| {
|
||||||
|
let command_tx = command_tx.clone();
|
||||||
|
let event_rx = event_rx.clone();
|
||||||
|
stream::channel(
|
||||||
|
1,
|
||||||
|
move |mut output: cosmic::iced::futures::channel::mpsc::Sender<
|
||||||
|
MounterMessage,
|
||||||
|
>| async move {
|
||||||
command_tx.send(Cmd::Rescan).unwrap();
|
command_tx.send(Cmd::Rescan).unwrap();
|
||||||
while let Some(event) = event_rx.lock().await.recv().await {
|
while let Some(event) = event_rx.lock().await.recv().await {
|
||||||
match event {
|
match event {
|
||||||
|
|
@ -693,7 +715,9 @@ impl Mounter for Gvfs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pending().await
|
pending().await
|
||||||
}),
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use cosmic::{
|
||||||
iced_core::{
|
iced_core::{
|
||||||
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
|
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
|
||||||
border::Border,
|
border::Border,
|
||||||
event::{self, Event},
|
event::Event,
|
||||||
layout,
|
layout,
|
||||||
mouse::{self, click},
|
mouse::{self, click},
|
||||||
overlay,
|
overlay,
|
||||||
|
|
@ -345,51 +345,52 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
self.content
|
self.content
|
||||||
.as_widget()
|
.as_widget_mut()
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn Operation,
|
operation: &mut dyn Operation,
|
||||||
) {
|
) {
|
||||||
self.content
|
self.content
|
||||||
.as_widget()
|
.as_widget_mut()
|
||||||
.operate(&mut tree.children[0], layout, renderer, operation);
|
.operate(&mut tree.children[0], layout, renderer, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: Event,
|
event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
clipboard: &mut dyn Clipboard,
|
clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
if self.content.as_widget_mut().on_event(
|
self.content.as_widget_mut().update(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
event.clone(),
|
event,
|
||||||
layout,
|
layout,
|
||||||
cursor,
|
cursor,
|
||||||
renderer,
|
renderer,
|
||||||
clipboard,
|
clipboard,
|
||||||
shell,
|
shell,
|
||||||
viewport,
|
viewport,
|
||||||
) == event::Status::Captured
|
);
|
||||||
{
|
|
||||||
return event::Status::Captured;
|
if shell.is_event_captured() {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(
|
update(
|
||||||
|
|
@ -400,7 +401,7 @@ where
|
||||||
shell,
|
shell,
|
||||||
tree.state.downcast_mut::<State>(),
|
tree.state.downcast_mut::<State>(),
|
||||||
viewport,
|
viewport,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_interaction(
|
fn mouse_interaction(
|
||||||
|
|
@ -468,13 +469,18 @@ where
|
||||||
fn overlay<'b>(
|
fn overlay<'b>(
|
||||||
&'b mut self,
|
&'b mut self,
|
||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'b>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
self.content
|
self.content.as_widget_mut().overlay(
|
||||||
.as_widget_mut()
|
&mut tree.children[0],
|
||||||
.overlay(&mut tree.children[0], layout, renderer, translation)
|
layout,
|
||||||
|
renderer,
|
||||||
|
viewport,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drag_destinations(
|
fn drag_destinations(
|
||||||
|
|
@ -522,7 +528,7 @@ fn update<Message: Clone>(
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
let offset = layout.virtual_offset();
|
let offset = layout.virtual_offset();
|
||||||
let layout_bounds = layout.bounds();
|
let layout_bounds = layout.bounds();
|
||||||
|
|
||||||
|
|
@ -590,7 +596,7 @@ fn update<Message: Clone>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
|
||||||
return event::Status::Ignored;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||||
|
|
@ -620,7 +626,8 @@ fn update<Message: Clone>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if widget.on_press.is_some() {
|
if widget.on_press.is_some() {
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -653,13 +660,14 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
if !recent_click {
|
if !recent_click {
|
||||||
state.prev_click = None;
|
state.prev_click = None;
|
||||||
return event::Status::Ignored;
|
return;
|
||||||
}
|
}
|
||||||
state.drag_initiated = None;
|
state.drag_initiated = None;
|
||||||
if let Some(message) = widget.on_release.as_ref() {
|
if let Some(message) = widget.on_release.as_ref() {
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -681,9 +689,10 @@ fn update<Message: Clone>(
|
||||||
shell.publish(message(point_opt));
|
shell.publish(message(point_opt));
|
||||||
|
|
||||||
if widget.on_right_press_no_capture {
|
if widget.on_right_press_no_capture {
|
||||||
return event::Status::Ignored;
|
return;
|
||||||
}
|
}
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_right_release.as_ref()
|
if let Some(message) = widget.on_right_release.as_ref()
|
||||||
|
|
@ -694,7 +703,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_middle_press.as_ref()
|
if let Some(message) = widget.on_middle_press.as_ref()
|
||||||
|
|
@ -705,7 +715,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_middle_release.as_ref()
|
if let Some(message) = widget.on_middle_release.as_ref()
|
||||||
|
|
@ -716,7 +727,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_back_press.as_ref()
|
if let Some(message) = widget.on_back_press.as_ref()
|
||||||
|
|
@ -727,7 +739,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_back_release.as_ref()
|
if let Some(message) = widget.on_back_release.as_ref()
|
||||||
|
|
@ -738,7 +751,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_forward_press.as_ref()
|
if let Some(message) = widget.on_forward_press.as_ref()
|
||||||
|
|
@ -749,7 +763,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(message) = widget.on_forward_release.as_ref()
|
if let Some(message) = widget.on_forward_release.as_ref()
|
||||||
|
|
@ -760,7 +775,8 @@ fn update<Message: Clone>(
|
||||||
{
|
{
|
||||||
shell.publish(message(cursor.position_in(layout_bounds)));
|
shell.publish(message(cursor.position_in(layout_bounds)));
|
||||||
|
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(on_scroll) = widget.on_scroll.as_ref()
|
if let Some(on_scroll) = widget.on_scroll.as_ref()
|
||||||
|
|
@ -768,7 +784,8 @@ fn update<Message: Clone>(
|
||||||
&& let Some(message) = on_scroll(*delta)
|
&& let Some(message) = on_scroll(*delta)
|
||||||
{
|
{
|
||||||
shell.publish(message);
|
shell.publish(message);
|
||||||
return event::Status::Captured;
|
shell.capture_event();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
|
if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
|
||||||
|
|
@ -780,6 +797,4 @@ fn update<Message: Clone>(
|
||||||
},
|
},
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
event::Status::Ignored
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
457
src/tab.rs
457
src/tab.rs
|
|
@ -4,16 +4,7 @@ use cosmic::{
|
||||||
desktop::fde::{DesktopEntry, get_languages_from_env},
|
desktop::fde::{DesktopEntry, get_languages_from_env},
|
||||||
font,
|
font,
|
||||||
iced::{
|
iced::{
|
||||||
Alignment,
|
Alignment, Border, Color, ContentFit, Length, Point, Rectangle, Size, Subscription, Vector,
|
||||||
Border,
|
|
||||||
Color,
|
|
||||||
ContentFit,
|
|
||||||
Length,
|
|
||||||
Point,
|
|
||||||
Rectangle,
|
|
||||||
Size,
|
|
||||||
Subscription,
|
|
||||||
Vector,
|
|
||||||
advanced::{
|
advanced::{
|
||||||
graphics,
|
graphics,
|
||||||
text::{self, Paragraph},
|
text::{self, Paragraph},
|
||||||
|
|
@ -23,11 +14,9 @@ use cosmic::{
|
||||||
event,
|
event,
|
||||||
futures::{self, SinkExt},
|
futures::{self, SinkExt},
|
||||||
keyboard::Modifiers,
|
keyboard::Modifiers,
|
||||||
padding,
|
padding, stream,
|
||||||
stream,
|
|
||||||
//TODO: export in cosmic::widget
|
|
||||||
widget::{
|
widget::{
|
||||||
horizontal_rule, rule,
|
rule,
|
||||||
scrollable::{self, AbsoluteOffset, Viewport},
|
scrollable::{self, AbsoluteOffset, Viewport},
|
||||||
stack,
|
stack,
|
||||||
},
|
},
|
||||||
|
|
@ -36,8 +25,9 @@ use cosmic::{
|
||||||
iced_core::{mouse::ScrollDelta, widget::tree},
|
iced_core::{mouse::ScrollDelta, widget::tree},
|
||||||
theme,
|
theme,
|
||||||
widget::{
|
widget::{
|
||||||
self, DndDestination, DndSource, Id, Space, Widget,
|
self, DndDestination, DndSource, Id, RcElementWrapper, Space, Widget,
|
||||||
menu::{action::MenuAction, key_bind::KeyBind},
|
menu::{action::MenuAction, key_bind::KeyBind},
|
||||||
|
space,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use i18n_embed::LanguageLoader;
|
use i18n_embed::LanguageLoader;
|
||||||
|
|
@ -57,7 +47,7 @@ use rustc_hash::FxHashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::{Cell, RefCell},
|
cell::Cell,
|
||||||
cmp::{Ordering, Reverse},
|
cmp::{Ordering, Reverse},
|
||||||
collections::{BTreeMap, HashMap},
|
collections::{BTreeMap, HashMap},
|
||||||
error::Error,
|
error::Error,
|
||||||
|
|
@ -67,7 +57,6 @@ use std::{
|
||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
os::unix::fs::MetadataExt,
|
os::unix::fs::MetadataExt,
|
||||||
path::{self, Path, PathBuf},
|
path::{self, Path, PathBuf},
|
||||||
rc::Rc,
|
|
||||||
sync::{Arc, LazyLock, RwLock, atomic},
|
sync::{Arc, LazyLock, RwLock, atomic},
|
||||||
time::{Duration, Instant, SystemTime},
|
time::{Duration, Instant, SystemTime},
|
||||||
};
|
};
|
||||||
|
|
@ -2968,6 +2957,11 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_items(&mut self, mut items: Vec<Item>) {
|
pub fn set_items(&mut self, mut items: Vec<Item>) {
|
||||||
|
let highlighted = self
|
||||||
|
.items_opt
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|items| items.iter().enumerate().find(|i| i.1.highlighted))
|
||||||
|
.map(|(i, _)| i);
|
||||||
let selected = self.selected_locations();
|
let selected = self.selected_locations();
|
||||||
for item in &mut items {
|
for item in &mut items {
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
|
|
@ -2978,6 +2972,12 @@ impl Tab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.items_opt = Some(items);
|
self.items_opt = Some(items);
|
||||||
|
if let Some(i) = highlighted
|
||||||
|
.zip(self.items_opt.as_mut())
|
||||||
|
.and_then(|(h, items)| items.get_mut(h))
|
||||||
|
{
|
||||||
|
i.highlighted = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cut_selected(&mut self) {
|
pub fn cut_selected(&mut self) {
|
||||||
|
|
@ -3813,7 +3813,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -3877,7 +3884,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -3928,7 +3942,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -3962,7 +3983,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -4000,7 +4028,14 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -4192,7 +4227,14 @@ impl Tab {
|
||||||
&& let Some(offset) = self.select_focus_scroll()
|
&& let Some(offset) = self.select_focus_scroll()
|
||||||
{
|
{
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4217,7 +4259,14 @@ impl Tab {
|
||||||
Message::ScrollToFocused => {
|
Message::ScrollToFocused => {
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4293,7 +4342,14 @@ impl Tab {
|
||||||
if self.select_position(0, 0, mod_shift) {
|
if self.select_position(0, 0, mod_shift) {
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -4308,7 +4364,14 @@ impl Tab {
|
||||||
{
|
{
|
||||||
if let Some(offset) = self.select_focus_scroll() {
|
if let Some(offset) = self.select_focus_scroll() {
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(offset.x),
|
||||||
|
y: Some(offset.y),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(id) = self.select_focus_id() {
|
if let Some(id) = self.select_focus_id() {
|
||||||
|
|
@ -4497,7 +4560,14 @@ impl Tab {
|
||||||
let offset = AbsoluteOffset { x: 0.0, y: 0.0 };
|
let offset = AbsoluteOffset { x: 0.0, y: 0.0 };
|
||||||
self.scroll_opt = Some(offset);
|
self.scroll_opt = Some(offset);
|
||||||
commands.push(Command::Iced(
|
commands.push(Command::Iced(
|
||||||
scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
|
scrollable::scroll_to(
|
||||||
|
self.scrollable_id.clone(),
|
||||||
|
AbsoluteOffset {
|
||||||
|
x: Some(0.0),
|
||||||
|
y: Some(0.0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4796,7 +4866,7 @@ impl Tab {
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
//TODO: use widget::image::viewer, when its zoom can be reset
|
//TODO: use widget::image::viewer, when its zoom can be reset
|
||||||
widget::image(image_handle).into()
|
crate::load_image::loaded_image(image_handle).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
element_opt = Some(widget::container(content).center(Length::Fill).into());
|
element_opt = Some(widget::container(content).center(Length::Fill).into());
|
||||||
|
|
@ -4822,20 +4892,20 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut column = widget::column::with_capacity(2);
|
let mut column = widget::column::with_capacity(2);
|
||||||
column = column.push(widget::Space::with_height(Length::Fixed(space_m.into())));
|
column = column.push(widget::space::vertical().height(Length::Fixed(space_m.into())));
|
||||||
{
|
{
|
||||||
let mut row = widget::row::with_capacity(5).align_y(Alignment::Center);
|
let mut row = widget::row::with_capacity(5).align_y(Alignment::Center);
|
||||||
row = row.push(widget::horizontal_space());
|
row = row.push(widget::space::horizontal());
|
||||||
if let Some(name) = name_opt {
|
if let Some(name) = name_opt {
|
||||||
row = row.push(name);
|
row = row.push(name);
|
||||||
}
|
}
|
||||||
row = row.push(widget::horizontal_space());
|
row = row.push(widget::space::horizontal());
|
||||||
row = row.push(
|
row = row.push(
|
||||||
widget::button::icon(widget::icon::from_name("window-close-symbolic"))
|
widget::button::icon(widget::icon::from_name("window-close-symbolic"))
|
||||||
.class(theme::Button::Standard)
|
.class(theme::Button::Standard)
|
||||||
.on_press(Message::Gallery(false)),
|
.on_press(Message::Gallery(false)),
|
||||||
);
|
);
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_m.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_m.into())));
|
||||||
// This mouse area provides window drag while the header bar is hidden
|
// This mouse area provides window drag while the header bar is hidden
|
||||||
let mouse_area = mouse_area::MouseArea::new(row)
|
let mouse_area = mouse_area::MouseArea::new(row)
|
||||||
.on_press(|_| Message::WindowDrag)
|
.on_press(|_| Message::WindowDrag)
|
||||||
|
|
@ -4844,28 +4914,29 @@ impl Tab {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let mut row = widget::row::with_capacity(7).align_y(Alignment::Center);
|
let mut row = widget::row::with_capacity(7).align_y(Alignment::Center);
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_m.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_m.into())));
|
||||||
row = row.push(
|
row = row.push(
|
||||||
widget::button::icon(widget::icon::from_name("go-previous-symbolic"))
|
widget::button::icon(widget::icon::from_name("go-previous-symbolic"))
|
||||||
.padding(space_xs)
|
.padding(space_xs)
|
||||||
.class(theme::Button::Standard)
|
.class(theme::Button::Standard)
|
||||||
.on_press(Message::GalleryPrevious),
|
.on_press(Message::GalleryPrevious),
|
||||||
);
|
);
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_xxs.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_xxs.into())));
|
||||||
if let Some(element) = element_opt {
|
if let Some(element) = element_opt {
|
||||||
row = row.push(element);
|
row = row.push(element);
|
||||||
} else {
|
} else {
|
||||||
//TODO: what to do when no image?
|
//TODO: what to do when no image?
|
||||||
row = row.push(widget::Space::new(Length::Fill, Length::Fill));
|
row = row.push(space::horizontal().width(Length::Fill));
|
||||||
|
row = row.push(space::vertical().height(Length::Fill));
|
||||||
}
|
}
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_xxs.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_xxs.into())));
|
||||||
row = row.push(
|
row = row.push(
|
||||||
widget::button::icon(widget::icon::from_name("go-next-symbolic"))
|
widget::button::icon(widget::icon::from_name("go-next-symbolic"))
|
||||||
.padding(space_xs)
|
.padding(space_xs)
|
||||||
.class(theme::Button::Standard)
|
.class(theme::Button::Standard)
|
||||||
.on_press(Message::GalleryNext),
|
.on_press(Message::GalleryNext),
|
||||||
);
|
);
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_m.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_m.into())));
|
||||||
column = column.push(row);
|
column = column.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4894,12 +4965,12 @@ impl Tab {
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
let text: text::Text<&'a str, font::Font> = text::Text {
|
let text: text::Text<&'a str, font::Font> = text::Text {
|
||||||
content,
|
content,
|
||||||
bounds: Size::INFINITY,
|
bounds: Size::INFINITE,
|
||||||
size: font_size.into(),
|
size: font_size.into(),
|
||||||
line_height: text::LineHeight::Absolute(line_height.into()),
|
line_height: text::LineHeight::Absolute(line_height.into()),
|
||||||
font,
|
font,
|
||||||
horizontal_alignment: Horizontal::Left,
|
align_x: text::Alignment::Left,
|
||||||
vertical_alignment: Vertical::Top,
|
align_y: Vertical::Top,
|
||||||
shaping: text::Shaping::default(),
|
shaping: text::Shaping::default(),
|
||||||
wrapping: text::Wrapping::None,
|
wrapping: text::Wrapping::None,
|
||||||
ellipsize: text::Ellipsize::End(text::EllipsizeHeightLimit::Lines(1)),
|
ellipsize: text::Ellipsize::End(text::EllipsizeHeightLimit::Lines(1)),
|
||||||
|
|
@ -4951,7 +5022,7 @@ impl Tab {
|
||||||
row = row.push(next_button);
|
row = row.push(next_button);
|
||||||
w += f32::from(space_xxs).mul_add(2.0, 16.0);
|
w += f32::from(space_xxs).mul_add(2.0, 16.0);
|
||||||
|
|
||||||
row = row.push(widget::Space::with_width(Length::Fixed(space_s.into())));
|
row = row.push(widget::space::horizontal().width(Length::Fixed(space_s.into())));
|
||||||
w += f32::from(space_s);
|
w += f32::from(space_s);
|
||||||
|
|
||||||
//TODO: allow resizing?
|
//TODO: allow resizing?
|
||||||
|
|
@ -5004,13 +5075,13 @@ impl Tab {
|
||||||
.padding([0, space_xxs]);
|
.padding([0, space_xxs]);
|
||||||
|
|
||||||
let accent_rule =
|
let accent_rule =
|
||||||
horizontal_rule(1).class(theme::Rule::Custom(Box::new(|theme| rule::Style {
|
rule::horizontal(1).class(theme::Rule::Custom(Box::new(|theme| rule::Style {
|
||||||
color: theme.cosmic().accent_color().into(),
|
color: theme.cosmic().accent_color().into(),
|
||||||
width: 1,
|
|
||||||
radius: 0.0.into(),
|
radius: 0.0.into(),
|
||||||
fill_mode: rule::FillMode::Full,
|
fill_mode: rule::FillMode::Full,
|
||||||
|
snap: true,
|
||||||
})));
|
})));
|
||||||
let heading_rule = widget::container(horizontal_rule(1))
|
let heading_rule = widget::container(rule::horizontal(1))
|
||||||
.padding([0, theme::active().cosmic().corner_radii.radius_xs[0] as u16]);
|
.padding([0, theme::active().cosmic().corner_radii.radius_xs[0] as u16]);
|
||||||
|
|
||||||
if let Some(edit_location) = &self.edit_location {
|
if let Some(edit_location) = &self.edit_location {
|
||||||
|
|
@ -5549,9 +5620,9 @@ impl Tab {
|
||||||
|
|
||||||
let spacer_height = height.saturating_sub(max_bottom + top_deduct);
|
let spacer_height = height.saturating_sub(max_bottom + top_deduct);
|
||||||
if spacer_height > 0 {
|
if spacer_height > 0 {
|
||||||
column = column.push(widget::container(Space::with_height(Length::Fixed(
|
column = column.push(widget::container(
|
||||||
spacer_height as f32,
|
space::vertical().height(Length::Fixed(spacer_height as f32)),
|
||||||
))));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5612,7 +5683,7 @@ impl Tab {
|
||||||
dnd_item_i += 1;
|
dnd_item_i += 1;
|
||||||
} else {
|
} else {
|
||||||
dnd_grid = dnd_grid.push(
|
dnd_grid = dnd_grid.push(
|
||||||
widget::container(Space::with_height(item_width as f32))
|
widget::container(space::vertical().height(item_width as f32))
|
||||||
.height(Length::Fixed(item_height as f32)),
|
.height(Length::Fixed(item_height as f32)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -5701,7 +5772,7 @@ impl Tab {
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
column = column
|
column = column
|
||||||
.push(widget::container(horizontal_rule(1)).padding([0, rule_padding]));
|
.push(widget::container(rule::horizontal(1)).padding([0, rule_padding]));
|
||||||
y += 1.0;
|
y += 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5899,7 +5970,9 @@ impl Tab {
|
||||||
|
|
||||||
if item.selected || !drag_items.is_empty() {
|
if item.selected || !drag_items.is_empty() {
|
||||||
let dnd_row = if !item.selected {
|
let dnd_row = if !item.selected {
|
||||||
Element::from(Space::with_height(Length::Fixed(f32::from(row_height))))
|
Element::from(
|
||||||
|
space::vertical().height(Length::Fixed(f32::from(row_height))),
|
||||||
|
)
|
||||||
} else if condensed {
|
} else if condensed {
|
||||||
widget::row::with_children([
|
widget::row::with_children([
|
||||||
widget::icon::icon(item.icon_handle_list_condensed.clone())
|
widget::icon::icon(item.icon_handle_list_condensed.clone())
|
||||||
|
|
@ -6006,7 +6079,7 @@ impl Tab {
|
||||||
|
|
||||||
let spacer_height = size.height - y - f32::from(top_deduct);
|
let spacer_height = size.height - y - f32::from(top_deduct);
|
||||||
if spacer_height > 0. {
|
if spacer_height > 0. {
|
||||||
column = column.push(widget::container(Space::with_height(spacer_height)));
|
column = column.push(widget::container(space::vertical().height(spacer_height)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let drag_col = (!drag_items.is_empty())
|
let drag_col = (!drag_items.is_empty())
|
||||||
|
|
@ -6075,7 +6148,7 @@ impl Tab {
|
||||||
let view = self.config.view;
|
let view = self.config.view;
|
||||||
let item_view = match drag_list {
|
let item_view = match drag_list {
|
||||||
Some(drag_list) if self.selected_clicked => {
|
Some(drag_list) if self.selected_clicked => {
|
||||||
let drag_list = RcElementWrapper::<Message>(Rc::new(RefCell::new(drag_list)));
|
let drag_list = RcElementWrapper::new(drag_list);
|
||||||
item_view
|
item_view
|
||||||
.drag_content(move || {
|
.drag_content(move || {
|
||||||
ClipboardCopy::new(crate::clipboard::ClipboardKind::Copy, &files)
|
ClipboardCopy::new(crate::clipboard::ClipboardKind::Copy, &files)
|
||||||
|
|
@ -6132,11 +6205,17 @@ impl Tab {
|
||||||
}
|
}
|
||||||
if can_scroll {
|
if can_scroll {
|
||||||
tab_column = tab_column.push(
|
tab_column = tab_column.push(
|
||||||
|
// FIXME: new responsive widget will remove the state from the scrollable
|
||||||
|
// id_container with custom id forces the state to be extracted in a diff
|
||||||
|
// pre-processing step
|
||||||
|
widget::id_container(
|
||||||
widget::scrollable(popover)
|
widget::scrollable(popover)
|
||||||
.id(self.scrollable_id.clone())
|
.id(self.scrollable_id.clone())
|
||||||
.on_scroll(Message::Scroll)
|
.on_scroll(Message::Scroll)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill),
|
.height(Length::Fill),
|
||||||
|
widget::Id::new(format!("{}-scrollable", self.scrollable_id)),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
tab_column = tab_column.push(popover);
|
tab_column = tab_column.push(popover);
|
||||||
|
|
@ -6148,7 +6227,7 @@ impl Tab {
|
||||||
{
|
{
|
||||||
tab_column = tab_column.push(
|
tab_column = tab_column.push(
|
||||||
widget::layer_container(widget::row::with_children([
|
widget::layer_container(widget::row::with_children([
|
||||||
widget::horizontal_space().into(),
|
widget::space::horizontal().into(),
|
||||||
widget::button::standard(fl!("empty-trash"))
|
widget::button::standard(fl!("empty-trash"))
|
||||||
.on_press(Message::EmptyTrash)
|
.on_press(Message::EmptyTrash)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -6163,7 +6242,7 @@ impl Tab {
|
||||||
Location::Network(uri, _display_name, _path) if uri == "network:///" => {
|
Location::Network(uri, _display_name, _path) if uri == "network:///" => {
|
||||||
tab_column = tab_column.push(
|
tab_column = tab_column.push(
|
||||||
widget::layer_container(widget::row::with_children([
|
widget::layer_container(widget::row::with_children([
|
||||||
widget::horizontal_space().into(),
|
widget::space::horizontal().into(),
|
||||||
widget::button::standard(fl!("add-network-drive"))
|
widget::button::standard(fl!("add-network-drive"))
|
||||||
.on_press(Message::AddNetworkDrive)
|
.on_press(Message::AddNetworkDrive)
|
||||||
.into(),
|
.into(),
|
||||||
|
|
@ -6342,7 +6421,14 @@ impl Tab {
|
||||||
clipboard_paste_available: bool,
|
clipboard_paste_available: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
widget::responsive(move |size| {
|
widget::responsive(move |size| {
|
||||||
self.view_responsive(key_binds, modifiers, size, clipboard_paste_available)
|
widget::id_container(
|
||||||
|
self.view_responsive(key_binds, modifiers, size, clipboard_paste_available),
|
||||||
|
Id::new(format!(
|
||||||
|
"tab-{}-{}",
|
||||||
|
self.scrollable_id, self.location_title
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
})
|
})
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
@ -6413,9 +6499,43 @@ impl Tab {
|
||||||
(max_mb, max_jobs)
|
(max_mb, max_jobs)
|
||||||
};
|
};
|
||||||
|
|
||||||
subscriptions.push(Subscription::run_with_id(
|
#[derive(Clone)]
|
||||||
("thumbnail", path.clone()),
|
struct Wrapper {
|
||||||
stream::channel(1, move |mut output| async move {
|
path: PathBuf,
|
||||||
|
metadata: ItemMetadata,
|
||||||
|
mime: mime::Mime,
|
||||||
|
effective_max_mb: u64,
|
||||||
|
effective_jobs: usize,
|
||||||
|
max_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Wrapper {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.path.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions.push(Subscription::run_with(
|
||||||
|
Wrapper {
|
||||||
|
path: path.clone(),
|
||||||
|
metadata,
|
||||||
|
mime,
|
||||||
|
effective_max_mb,
|
||||||
|
effective_jobs,
|
||||||
|
max_size,
|
||||||
|
},
|
||||||
|
|wrapper| {
|
||||||
|
let Wrapper {
|
||||||
|
path,
|
||||||
|
metadata,
|
||||||
|
mime,
|
||||||
|
effective_max_mb,
|
||||||
|
effective_jobs,
|
||||||
|
max_size,
|
||||||
|
} = wrapper.clone();
|
||||||
|
stream::channel(
|
||||||
|
1,
|
||||||
|
move |mut output: futures::channel::mpsc::Sender<_>| async move {
|
||||||
let message = {
|
let message = {
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
|
|
||||||
|
|
@ -6456,7 +6576,9 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
}),
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6481,10 +6603,21 @@ impl Tab {
|
||||||
if let Some(path) = item.path_opt().cloned() {
|
if let Some(path) = item.path_opt().cloned() {
|
||||||
// Item must be calculating directory size
|
// Item must be calculating directory size
|
||||||
if let DirSize::Calculating(controller) = &item.dir_size {
|
if let DirSize::Calculating(controller) = &item.dir_size {
|
||||||
|
struct Wrapper {
|
||||||
|
path: PathBuf,
|
||||||
|
controller: Controller,
|
||||||
|
}
|
||||||
|
impl Hash for Wrapper {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.path.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscriptions.push(Subscription::run_with(
|
||||||
|
Wrapper { path: path.clone(), controller: controller.clone() },
|
||||||
|
|Wrapper { path, controller }| {
|
||||||
|
let path = path.clone();
|
||||||
let controller = controller.clone();
|
let controller = controller.clone();
|
||||||
subscriptions.push(Subscription::run_with_id(
|
stream::channel(1, |mut output: futures::channel::mpsc::Sender<_>| async move {
|
||||||
("dir_size", path.clone()),
|
|
||||||
stream::channel(1, |mut output| async move {
|
|
||||||
let message = {
|
let message = {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
match calculate_dir_size(&path, controller).await {
|
match calculate_dir_size(&path, controller).await {
|
||||||
|
|
@ -6525,7 +6658,8 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
}),
|
})
|
||||||
|
}
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -6540,9 +6674,35 @@ impl Tab {
|
||||||
let term = term.clone();
|
let term = term.clone();
|
||||||
let show_hidden = *show_hidden;
|
let show_hidden = *show_hidden;
|
||||||
let start = *start;
|
let start = *start;
|
||||||
subscriptions.push(Subscription::run_with_id(
|
#[derive(Debug, Hash, Clone)]
|
||||||
location.clone(),
|
struct Wrapper {
|
||||||
stream::channel(2, move |mut output| async move {
|
location: Location,
|
||||||
|
search_location: SearchLocation,
|
||||||
|
term: String,
|
||||||
|
show_hidden: bool,
|
||||||
|
start: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions.push(Subscription::run_with(
|
||||||
|
Wrapper {
|
||||||
|
location: location.clone(),
|
||||||
|
search_location: search_location.clone(),
|
||||||
|
term: term.clone(),
|
||||||
|
show_hidden,
|
||||||
|
start,
|
||||||
|
},
|
||||||
|
|wrapper| {
|
||||||
|
let wrapper = wrapper.clone();
|
||||||
|
stream::channel(
|
||||||
|
2,
|
||||||
|
move |mut output: futures::channel::mpsc::Sender<Message>| async move {
|
||||||
|
let Wrapper {
|
||||||
|
location,
|
||||||
|
search_location,
|
||||||
|
term,
|
||||||
|
show_hidden,
|
||||||
|
start,
|
||||||
|
} = wrapper;
|
||||||
//TODO: optimal size?
|
//TODO: optimal size?
|
||||||
let (results_tx, results_rx) = mpsc::channel(65536);
|
let (results_tx, results_rx) = mpsc::channel(65536);
|
||||||
|
|
||||||
|
|
@ -6570,9 +6730,12 @@ impl Tab {
|
||||||
show_hidden,
|
show_hidden,
|
||||||
move |search_item| -> bool {
|
move |search_item| -> bool {
|
||||||
// Don't send if the result is too old
|
// Don't send if the result is too old
|
||||||
if let Some(last_modified) = *last_modified_opt.read().unwrap()
|
if let Some(last_modified) =
|
||||||
|
*last_modified_opt.read().unwrap()
|
||||||
|
{
|
||||||
|
if let SearchItem::Path(_, _, ref metadata) =
|
||||||
|
search_item
|
||||||
{
|
{
|
||||||
if let SearchItem::Path(_, _, ref metadata) = search_item {
|
|
||||||
if let Ok(modified) = metadata.modified() {
|
if let Ok(modified) = metadata.modified() {
|
||||||
if modified < last_modified {
|
if modified < last_modified {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -6618,7 +6781,9 @@ impl Tab {
|
||||||
let _ = output.lock().await.send(Message::SearchReady(true)).await;
|
let _ = output.lock().await.send(Message::SearchReady(true)).await;
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
}),
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6628,9 +6793,13 @@ impl Tab {
|
||||||
.and_then(|x| x.location.path_opt())
|
.and_then(|x| x.location.path_opt())
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
subscriptions.push(Subscription::run_with_id(
|
subscriptions.push(Subscription::run_with(
|
||||||
("tab_complete", path.to_string_lossy().into_owned()),
|
("tab_complete", path.clone()),
|
||||||
stream::channel(1, |mut output| async move {
|
|(_, path)| {
|
||||||
|
let path = path.clone();
|
||||||
|
stream::channel(
|
||||||
|
1,
|
||||||
|
|mut output: futures::channel::mpsc::Sender<_>| async move {
|
||||||
let message = {
|
let message = {
|
||||||
let path = path.clone();
|
let path = path.clone();
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
|
|
@ -6670,7 +6839,9 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::future::pending().await
|
std::future::pending().await
|
||||||
}),
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6703,141 +6874,6 @@ pub fn respond_to_scroll_direction(delta: ScrollDelta, modifiers: &Modifiers) ->
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RcElementWrapper<M>(pub Rc<RefCell<Element<'static, M>>>);
|
|
||||||
|
|
||||||
impl<M> Widget<M, cosmic::Theme, cosmic::Renderer> for RcElementWrapper<M> {
|
|
||||||
fn size(&self) -> Size<Length> {
|
|
||||||
self.0.borrow().as_widget().size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> Size<Length> {
|
|
||||||
self.0.borrow().as_widget().size_hint()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&self,
|
|
||||||
tree: &mut tree::Tree,
|
|
||||||
renderer: &cosmic::Renderer,
|
|
||||||
limits: &cosmic::iced_core::layout::Limits,
|
|
||||||
) -> cosmic::iced_core::layout::Node {
|
|
||||||
self.0.borrow().as_widget().layout(tree, renderer, limits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
tree: &tree::Tree,
|
|
||||||
renderer: &mut cosmic::Renderer,
|
|
||||||
theme: &cosmic::Theme,
|
|
||||||
style: &cosmic::iced_core::renderer::Style,
|
|
||||||
layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
cursor: cosmic::iced_core::mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) {
|
|
||||||
self.0
|
|
||||||
.borrow()
|
|
||||||
.as_widget()
|
|
||||||
.draw(tree, renderer, theme, style, layout, cursor, viewport);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tag(&self) -> tree::Tag {
|
|
||||||
self.0.borrow().as_widget().tag()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
|
||||||
self.0.borrow().as_widget().state()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children(&self) -> Vec<tree::Tree> {
|
|
||||||
self.0.borrow().as_widget().children()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut tree::Tree) {
|
|
||||||
self.0.borrow_mut().as_widget_mut().diff(tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(
|
|
||||||
&self,
|
|
||||||
state: &mut tree::Tree,
|
|
||||||
layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
renderer: &cosmic::Renderer,
|
|
||||||
operation: &mut dyn widget::Operation,
|
|
||||||
) {
|
|
||||||
self.0
|
|
||||||
.borrow()
|
|
||||||
.as_widget()
|
|
||||||
.operate(state, layout, renderer, operation);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
&mut self,
|
|
||||||
_state: &mut tree::Tree,
|
|
||||||
_event: cosmic::iced::Event,
|
|
||||||
_layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
_cursor: cosmic::iced_core::mouse::Cursor,
|
|
||||||
_renderer: &cosmic::Renderer,
|
|
||||||
_clipboard: &mut dyn cosmic::iced_core::Clipboard,
|
|
||||||
_shell: &mut cosmic::iced_core::Shell<'_, M>,
|
|
||||||
_viewport: &Rectangle,
|
|
||||||
) -> event::Status {
|
|
||||||
self.0.borrow_mut().as_widget_mut().on_event(
|
|
||||||
_state, _event, _layout, _cursor, _renderer, _clipboard, _shell, _viewport,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
_state: &tree::Tree,
|
|
||||||
_layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
_cursor: cosmic::iced_core::mouse::Cursor,
|
|
||||||
_viewport: &Rectangle,
|
|
||||||
_renderer: &cosmic::Renderer,
|
|
||||||
) -> cosmic::iced_core::mouse::Interaction {
|
|
||||||
self.0
|
|
||||||
.borrow()
|
|
||||||
.as_widget()
|
|
||||||
.mouse_interaction(_state, _layout, _cursor, _viewport, _renderer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
_state: &'a mut tree::Tree,
|
|
||||||
_layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
_renderer: &cosmic::Renderer,
|
|
||||||
_translation: cosmic::iced_core::Vector,
|
|
||||||
) -> Option<cosmic::iced_core::overlay::Element<'a, M, cosmic::Theme, cosmic::Renderer>> {
|
|
||||||
// TODO
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn id(&self) -> Option<Id> {
|
|
||||||
self.0.borrow().as_widget().id()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_id(&mut self, _id: Id) {
|
|
||||||
self.0.borrow_mut().as_widget_mut().set_id(_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drag_destinations(
|
|
||||||
&self,
|
|
||||||
_state: &tree::Tree,
|
|
||||||
_layout: cosmic::iced_core::Layout<'_>,
|
|
||||||
renderer: &cosmic::Renderer,
|
|
||||||
_dnd_rectangles: &mut cosmic::iced_core::clipboard::DndDestinationRectangles,
|
|
||||||
) {
|
|
||||||
self.0
|
|
||||||
.borrow()
|
|
||||||
.as_widget()
|
|
||||||
.drag_destinations(_state, _layout, renderer, _dnd_rectangles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Message: 'static> From<RcElementWrapper<Message>> for Element<'static, Message> {
|
|
||||||
fn from(wrapper: RcElementWrapper<Message>) -> Self {
|
|
||||||
Element::new(wrapper)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_editor_class(
|
fn text_editor_class(
|
||||||
theme: &cosmic::Theme,
|
theme: &cosmic::Theme,
|
||||||
status: cosmic::widget::text_editor::Status,
|
status: cosmic::widget::text_editor::Status,
|
||||||
|
|
@ -6852,7 +6888,6 @@ fn text_editor_class(
|
||||||
let mut placeholder = cosmic.palette.neutral_9;
|
let mut placeholder = cosmic.palette.neutral_9;
|
||||||
placeholder.alpha = 0.7;
|
placeholder.alpha = 0.7;
|
||||||
let placeholder = placeholder.into();
|
let placeholder = placeholder.into();
|
||||||
let icon = cosmic.background.on.into();
|
|
||||||
|
|
||||||
match status {
|
match status {
|
||||||
cosmic::iced_widget::text_editor::Status::Active
|
cosmic::iced_widget::text_editor::Status::Active
|
||||||
|
|
@ -6864,14 +6899,13 @@ fn text_editor_class(
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
color: container.component.divider.into(),
|
color: container.component.divider.into(),
|
||||||
},
|
},
|
||||||
icon,
|
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
selection,
|
selection,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cosmic::iced_widget::text_editor::Status::Hovered
|
cosmic::iced_widget::text_editor::Status::Hovered
|
||||||
| cosmic::iced_widget::text_editor::Status::Focused => {
|
| cosmic::iced_widget::text_editor::Status::Focused { .. } => {
|
||||||
cosmic::iced_widget::text_editor::Style {
|
cosmic::iced_widget::text_editor::Style {
|
||||||
background: background.into(),
|
background: background.into(),
|
||||||
border: cosmic::iced::Border {
|
border: cosmic::iced::Border {
|
||||||
|
|
@ -6879,7 +6913,6 @@ fn text_editor_class(
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
color: cosmic::iced::Color::from(cosmic.accent.base),
|
color: cosmic::iced::Color::from(cosmic.accent.base),
|
||||||
},
|
},
|
||||||
icon,
|
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
selection,
|
selection,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue