feat: rebase libcosmic onto iced 0.14

This commit is contained in:
Ashley Wulber 2026-03-13 16:04:17 -04:00 • committed by GitHub
parent 03988df2dc
commit 360973175c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 2142 additions and 2014 deletions

2301
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -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" }

View file

@ -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
}), },
)
},
)); ));
} }
} }

View file

@ -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

View file

@ -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);

View file

@ -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
View 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)
}
}

View file

@ -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))
) )
}; };

View file

@ -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
}), },
)
},
) )
} }
} }

View file

@ -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
} }

View file

@ -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,