Merge pull request #618 from pop-os/libcosmic-rebase

Update libcosmic/iced
This commit is contained in:
Jeremy Soller 2024-10-22 08:22:07 -06:00 committed by GitHub
commit 2ae97c167c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1097 additions and 1116 deletions

1236
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -63,19 +63,18 @@ uzers = "0.12.0"
[dependencies.libcosmic] [dependencies.libcosmic]
git = "https://github.com/pop-os/libcosmic.git" git = "https://github.com/pop-os/libcosmic.git"
default-features = false default-features = false
features = ["a11y", "clipboard", "multi-window", "tokio"] features = ["a11y", "multi-window", "tokio", "winit"]
[dependencies.smol_str] [dependencies.smol_str]
version = "0.2.1" version = "0.2.1"
features = ["serde"] features = ["serde"]
[features] [features]
default = ["bzip2", "desktop", "gvfs", "liblzma", "notify", "winit", "wgpu"] default = ["bzip2", "desktop", "gvfs", "liblzma", "notify", "wgpu"]
desktop = ["libcosmic/desktop", "dep:xdg"] desktop = ["libcosmic/desktop", "dep:xdg"]
gvfs = ["dep:gio", "dep:glib"] gvfs = ["dep:gio", "dep:glib"]
notify = ["dep:notify-rust"] notify = ["dep:notify-rust"]
wayland = ["libcosmic/wayland", "dep:wayland-client"] wayland = ["libcosmic/wayland", "dep:wayland-client"]
winit = ["libcosmic/winit"]
wgpu = ["libcosmic/wgpu"] wgpu = ["libcosmic/wgpu"]
[profile.dev] [profile.dev]

View file

@ -1,7 +1,7 @@
use cosmic::{ use cosmic::{
app::{self, Command, Core, Settings}, app::{self, Core, Settings, Task},
executor, executor,
iced::{subscription::Subscription, window}, iced::{window, Subscription},
widget, Application, Element, widget, Application, Element,
}; };
use cosmic_files::dialog::{Dialog, DialogKind, DialogMessage, DialogResult}; use cosmic_files::dialog::{Dialog, DialogKind, DialogMessage, DialogResult};
@ -42,18 +42,18 @@ impl Application for App {
&mut self.core &mut self.core
} }
fn init(core: Core, _flags: Self::Flags) -> (Self, Command<Message>) { fn init(core: Core, _flags: Self::Flags) -> (Self, Task<Message>) {
( (
Self { Self {
core, core,
dialog_opt: None, dialog_opt: None,
result_opt: None, result_opt: None,
}, },
Command::none(), Task::none(),
) )
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::DialogMessage(dialog_message) => { Message::DialogMessage(dialog_message) => {
if let Some(dialog) = &mut self.dialog_opt { if let Some(dialog) = &mut self.dialog_opt {
@ -78,7 +78,7 @@ impl Application for App {
} }
} }
Command::none() Task::none()
} }
fn view_window(&self, window_id: window::Id) -> Element<Message> { fn view_window(&self, window_id: window::Id) -> Element<Message> {

View file

@ -4,26 +4,26 @@
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
use cosmic::iced::{ use cosmic::iced::{
event::wayland::{Event as WaylandEvent, OutputEvent}, event::wayland::{Event as WaylandEvent, OutputEvent},
wayland::{ platform_specific::runtime::wayland::layer_surface::{
actions::layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, IcedMargin, IcedOutput, SctkLayerSurfaceSettings,
layer_surface::{ },
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer, platform_specific::shell::wayland::commands::layer_surface::{
}, destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer,
}, },
Limits, Limits,
}; };
use cosmic::{ use cosmic::{
app::{self, message, Command, Core}, app::{self, message, Core, Task},
cosmic_config, cosmic_theme, executor, cosmic_config, cosmic_theme, executor,
iced::{ iced::{
clipboard::dnd::DndAction, clipboard::dnd::DndAction,
event, event,
futures::{self, SinkExt}, futures::{self, SinkExt},
keyboard::{Event as KeyEvent, Key, Modifiers}, keyboard::{Event as KeyEvent, Key, Modifiers},
subscription::{self, Subscription}, stream,
widget::scrollable, widget::scrollable,
window::{self, Event as WindowEvent, Id as WindowId}, window::{self, Event as WindowEvent, Id as WindowId},
Alignment, Event, Length, Size, Alignment, Event, Length, Size, Subscription,
}, },
iced_runtime::clipboard, iced_runtime::clipboard,
style, theme, style, theme,
@ -619,7 +619,7 @@ impl App {
location: Location, location: Location,
activate: bool, activate: bool,
selection_path: Option<PathBuf>, selection_path: Option<PathBuf>,
) -> (Entity, Command<Message>) { ) -> (Entity, Task<Message>) {
let mut tab = Tab::new(location.clone(), self.config.tab); let mut tab = Tab::new(location.clone(), self.config.tab);
tab.mode = match self.mode { tab.mode = match self.mode {
Mode::App => tab::Mode::App, Mode::App => tab::Mode::App,
@ -643,7 +643,7 @@ impl App {
( (
entity, entity,
Command::batch([ Task::batch([
self.update_title(), self.update_title(),
self.update_watcher(), self.update_watcher(),
self.rescan_tab(entity, location, selection_path), self.rescan_tab(entity, location, selection_path),
@ -656,7 +656,7 @@ impl App {
location: Location, location: Location,
activate: bool, activate: bool,
selection_path: Option<PathBuf>, selection_path: Option<PathBuf>,
) -> Command<Message> { ) -> Task<Message> {
self.open_tab_entity(location, activate, selection_path).1 self.open_tab_entity(location, activate, selection_path).1
} }
@ -681,10 +681,10 @@ impl App {
entity: Entity, entity: Entity,
location: Location, location: Location,
selection_path: Option<PathBuf>, selection_path: Option<PathBuf>,
) -> Command<Message> { ) -> Task<Message> {
log::info!("rescan_tab {entity:?} {location:?} {selection_path:?}"); log::info!("rescan_tab {entity:?} {location:?} {selection_path:?}");
let icon_sizes = self.config.tab.icon_sizes; let icon_sizes = self.config.tab.icon_sizes;
Command::perform( Task::perform(
async move { async move {
let location2 = location.clone(); let location2 = location.clone();
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await { match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
@ -705,7 +705,7 @@ impl App {
) )
} }
fn rescan_trash(&mut self) -> Command<Message> { fn rescan_trash(&mut self) -> Task<Message> {
let mut needs_reload = Vec::new(); let mut needs_reload = Vec::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(tab) = self.tab_model.data::<Tab>(entity) { if let Some(tab) = self.tab_model.data::<Tab>(entity) {
@ -719,14 +719,14 @@ impl App {
for (entity, location) in needs_reload { for (entity, location) in needs_reload {
commands.push(self.rescan_tab(entity, location, None)); commands.push(self.rescan_tab(entity, location, None));
} }
Command::batch(commands) Task::batch(commands)
} }
fn search(&mut self) -> Command<Message> { fn search(&mut self) -> Task<Message> {
if let Some(term) = self.search_get() { if let Some(term) = self.search_get() {
self.search_set(Some(term.to_string())) self.search_set(Some(term.to_string()))
} else { } else {
Command::none() Task::none()
} }
} }
@ -739,7 +739,7 @@ impl App {
} }
} }
fn search_set(&mut self, term_opt: Option<String>) -> Command<Message> { fn search_set(&mut self, term_opt: Option<String>) -> Task<Message> {
let entity = self.tab_model.active(); let entity = self.tab_model.active();
let mut title_location_opt = None; let mut title_location_opt = None;
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) { if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
@ -768,18 +768,18 @@ impl App {
} }
if let Some((title, location, focus_search)) = title_location_opt { if let Some((title, location, focus_search)) = title_location_opt {
self.tab_model.text_set(entity, title); self.tab_model.text_set(entity, title);
return Command::batch([ return Task::batch([
self.update_title(), self.update_title(),
self.update_watcher(), self.update_watcher(),
self.rescan_tab(entity, location, None), self.rescan_tab(entity, location, None),
if focus_search { if focus_search {
widget::text_input::focus(self.search_id.clone()) widget::text_input::focus(self.search_id.clone())
} else { } else {
Command::none() Task::none()
}, },
]); ]);
} }
Command::none() Task::none()
} }
fn selected_paths(&self, entity_opt: Option<Entity>) -> Vec<PathBuf> { fn selected_paths(&self, entity_opt: Option<Entity>) -> Vec<PathBuf> {
@ -795,7 +795,7 @@ impl App {
paths paths
} }
fn update_config(&mut self) -> Command<Message> { fn update_config(&mut self) -> Task<Message> {
self.update_nav_model(); self.update_nav_model();
// Tabs are collected first to placate the borrowck // Tabs are collected first to placate the borrowck
let tabs: Vec<_> = self.tab_model.iter().collect(); let tabs: Vec<_> = self.tab_model.iter().collect();
@ -810,10 +810,10 @@ impl App {
)) ))
})) }))
.collect(); .collect();
Command::batch(commands) Task::batch(commands)
} }
fn update_desktop(&mut self) -> Command<Message> { fn update_desktop(&mut self) -> Task<Message> {
let mut needs_reload = Vec::new(); let mut needs_reload = Vec::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
if let Some(tab) = self.tab_model.data::<Tab>(entity) { if let Some(tab) = self.tab_model.data::<Tab>(entity) {
@ -826,7 +826,7 @@ impl App {
for (entity, location) in needs_reload { for (entity, location) in needs_reload {
commands.push(self.rescan_tab(entity, location, None)); commands.push(self.rescan_tab(entity, location, None));
} }
Command::batch(commands) Task::batch(commands)
} }
fn activate_nav_model_location(&mut self, location: &Location) { fn activate_nav_model_location(&mut self, location: &Location) {
@ -941,12 +941,12 @@ impl App {
} }
} }
fn update_notification(&mut self) -> Command<Message> { fn update_notification(&mut self) -> Task<Message> {
// Handle closing notification if there are no operations // Handle closing notification if there are no operations
if self.pending_operations.is_empty() { if self.pending_operations.is_empty() {
#[cfg(feature = "notify")] #[cfg(feature = "notify")]
if let Some(notification_arc) = self.notification_opt.take() { if let Some(notification_arc) = self.notification_opt.take() {
return Command::perform( return Task::perform(
async move { async move {
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
//TODO: this is nasty //TODO: this is nasty
@ -963,18 +963,22 @@ impl App {
} }
} }
Command::none() Task::none()
} }
fn update_title(&mut self) -> Command<Message> { fn update_title(&mut self) -> Task<Message> {
let window_title = match self.tab_model.text(self.tab_model.active()) { let window_title = match self.tab_model.text(self.tab_model.active()) {
Some(tab_title) => format!("{tab_title}{}", fl!("cosmic-files")), Some(tab_title) => format!("{tab_title}{}", fl!("cosmic-files")),
None => fl!("cosmic-files"), None => fl!("cosmic-files"),
}; };
self.set_window_title(window_title, window::Id::MAIN) if let Some(window_id) = &self.window_id_opt {
self.set_window_title(window_title, *window_id)
} else {
Task::none()
}
} }
fn update_watcher(&mut self) -> Command<Message> { fn update_watcher(&mut self) -> Task<Message> {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() { if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = HashSet::new(); let mut new_paths = HashSet::new();
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {
@ -1021,7 +1025,7 @@ impl App {
} }
//TODO: should any of this run in a command? //TODO: should any of this run in a command?
Command::none() Task::none()
} }
fn about(&self) -> Element<Message> { fn about(&self) -> Element<Message> {
@ -1051,7 +1055,7 @@ impl App {
.padding(0) .padding(0)
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_x(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
} }
@ -1093,11 +1097,8 @@ impl App {
text_input.into(), text_input.into(),
widget::text(fl!("network-drive-description")).into(), widget::text(fl!("network-drive-description")).into(),
table.into(), table.into(),
widget::row::with_children(vec![ widget::row::with_children(vec![widget::horizontal_space().into(), button.into()])
widget::horizontal_space(Length::Fill).into(), .into(),
button.into(),
])
.into(),
]) ])
.spacing(space_m) .spacing(space_m)
.into() .into()
@ -1322,7 +1323,7 @@ impl Application for App {
} }
/// Creates the application, and optionally emits command on initialize. /// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) { fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
core.window.context_is_overlay = false; core.window.context_is_overlay = false;
match flags.mode { match flags.mode {
Mode::App => { Mode::App => {
@ -1346,6 +1347,8 @@ impl Application for App {
Mode::Desktop => tab::Mode::Desktop, Mode::Desktop => tab::Mode::Desktop,
}); });
let window_id_opt = core.main_window_id();
let mut app = App { let mut app = App {
core, core,
nav_bar_context_id: segmented_button::Entity::null(), nav_bar_context_id: segmented_button::Entity::null(),
@ -1376,7 +1379,7 @@ impl Application for App {
surface_names: HashMap::new(), surface_names: HashMap::new(),
toasts: widget::toaster::Toasts::new(Message::CloseToast), toasts: widget::toaster::Toasts::new(Message::CloseToast),
watcher_opt: None, watcher_opt: None,
window_id_opt: Some(window::Id::MAIN), window_id_opt,
windows: HashMap::new(), windows: HashMap::new(),
nav_dnd_hover: None, nav_dnd_hover: None,
tab_dnd_hover: None, tab_dnd_hover: None,
@ -1398,11 +1401,7 @@ impl Application for App {
} }
} }
(app, Command::batch(commands)) (app, Task::batch(commands))
}
fn main_window_id(&self) -> window::Id {
self.window_id_opt.unwrap_or(window::Id::MAIN)
} }
fn nav_bar(&self) -> Option<Element<message::Message<Self::Message>>> { fn nav_bar(&self) -> Option<Element<message::Message<Self::Message>>> {
@ -1504,7 +1503,7 @@ impl Application for App {
} }
} }
fn on_nav_select(&mut self, entity: Entity) -> Command<Self::Message> { fn on_nav_select(&mut self, entity: Entity) -> Task<Self::Message> {
self.nav_model.activate(entity); self.nav_model.activate(entity);
if let Some(location) = self.nav_model.data::<Location>(entity) { if let Some(location) = self.nav_model.data::<Location>(entity) {
let message = Message::TabMessage(None, tab::Message::Location(location.clone())); let message = Message::TabMessage(None, tab::Message::Location(location.clone()));
@ -1516,7 +1515,7 @@ impl Application for App {
return mounter.mount(data.1.clone()).map(|_| message::none()); return mounter.mount(data.1.clone()).map(|_| message::none());
} }
} }
Command::none() Task::none()
} }
fn on_app_exit(&mut self) -> Option<Message> { fn on_app_exit(&mut self) -> Option<Message> {
@ -1527,7 +1526,7 @@ impl Application for App {
Some(Message::WindowCloseRequested(id)) Some(Message::WindowCloseRequested(id))
} }
fn on_context_drawer(&mut self) -> Command<Self::Message> { fn on_context_drawer(&mut self) -> Task<Self::Message> {
match self.context_page { match self.context_page {
ContextPage::Preview(..) => { ContextPage::Preview(..) => {
// Persist state of preview page // Persist state of preview page
@ -1537,22 +1536,22 @@ impl Application for App {
} }
_ => {} _ => {}
} }
Command::none() Task::none()
} }
fn on_escape(&mut self) -> Command<Self::Message> { fn on_escape(&mut self) -> Task<Self::Message> {
let entity = self.tab_model.active(); let entity = self.tab_model.active();
// Close dialog if open // Close dialog if open
if self.dialog_pages.pop_front().is_some() { if self.dialog_pages.pop_front().is_some() {
return Command::none(); return Task::none();
} }
// Close gallery mode if open // Close gallery mode if open
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) { if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
if tab.gallery { if tab.gallery {
tab.gallery = false; tab.gallery = false;
return Command::none(); return Task::none();
} }
} }
@ -1562,7 +1561,7 @@ impl Application for App {
// of closing everything on one press // of closing everything on one press
if self.core.window.show_context { if self.core.window.show_context {
self.set_show_context(false); self.set_show_context(false);
return Command::none(); return Task::none();
} }
if self.search_get().is_some() { if self.search_get().is_some() {
// Close search if open // Close search if open
@ -1571,12 +1570,12 @@ impl Application for App {
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) { if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
if tab.context_menu.is_some() { if tab.context_menu.is_some() {
tab.context_menu = None; tab.context_menu = None;
return Command::none(); return Task::none();
} }
if tab.edit_location.is_some() { if tab.edit_location.is_some() {
tab.edit_location = None; tab.edit_location = None;
return Command::none(); return Task::none();
} }
let had_focused_button = tab.select_focus_id().is_some(); let had_focused_button = tab.select_focus_id().is_some();
@ -1585,15 +1584,15 @@ impl Application for App {
// Unfocus if there was a focused button // Unfocus if there was a focused button
return widget::button::focus(widget::Id::unique()); return widget::button::focus(widget::Id::unique());
} }
return Command::none(); return Task::none();
} }
} }
Command::none() Task::none()
} }
/// Handle application events here. /// Handle application events here.
fn update(&mut self, message: Self::Message) -> Command<Self::Message> { fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
// Helper for updating config values efficiently // Helper for updating config values efficiently
macro_rules! config_set { macro_rules! config_set {
($name: ident, $value: expr) => { ($name: ident, $value: expr) => {
@ -1709,9 +1708,9 @@ impl Application for App {
"com.system76.CosmicFilesDialog".to_string(); "com.system76.CosmicFilesDialog".to_string();
} }
let (id, command) = window::spawn(settings); let (id, command) = window::open(settings);
self.windows.insert(id, WindowKind::DesktopViewOptions); self.windows.insert(id, WindowKind::DesktopViewOptions);
return command; return command.map(|_id| message::none());
} }
Message::DialogCancel => { Message::DialogCancel => {
self.dialog_pages.pop_front(); self.dialog_pages.pop_front();
@ -1755,7 +1754,7 @@ impl Application for App {
auth, auth,
auth_tx, auth_tx,
} => { } => {
return Command::perform( return Task::perform(
async move { async move {
auth_tx.send(auth).await.unwrap(); auth_tx.send(auth).await.unwrap();
message::none() message::none()
@ -1769,7 +1768,7 @@ impl Application for App {
error: _, error: _,
} => { } => {
//TODO: re-use mounter_key? //TODO: re-use mounter_key?
return Command::batch([ return Task::batch([
self.update(Message::NetworkDriveInput(uri)), self.update(Message::NetworkDriveInput(uri)),
self.update(Message::NetworkDriveSubmit), self.update(Message::NetworkDriveSubmit),
]); ]);
@ -1841,7 +1840,7 @@ impl Application for App {
} }
} }
Message::DialogUpdateComplete(dialog_page) => { Message::DialogUpdateComplete(dialog_page) => {
return Command::batch([ return Task::batch([
self.update(Message::DialogUpdate(dialog_page)), self.update(Message::DialogUpdate(dialog_page)),
self.update(Message::DialogComplete), self.update(Message::DialogComplete),
]); ]);
@ -1951,7 +1950,7 @@ impl Application for App {
// Update desktop tabs // Update desktop tabs
commands.push(self.update_desktop()); commands.push(self.update_desktop());
return Command::batch(commands); return Task::batch(commands);
} }
Message::MountResult(mounter_key, item, res) => match res { Message::MountResult(mounter_key, item, res) => match res {
Ok(true) => { Ok(true) => {
@ -2099,7 +2098,7 @@ impl Application for App {
for (entity, location) in needs_reload { for (entity, location) in needs_reload {
commands.push(self.rescan_tab(entity, location, None)); commands.push(self.rescan_tab(entity, location, None));
} }
return Command::batch(commands); return Task::batch(commands);
} }
Message::NotifyWatcher(mut watcher_wrapper) => match watcher_wrapper.watcher_opt.take() Message::NotifyWatcher(mut watcher_wrapper) => match watcher_wrapper.watcher_opt.take()
{ {
@ -2152,7 +2151,7 @@ impl Application for App {
} }
} }
Message::OpenInNewTab(entity_opt) => { Message::OpenInNewTab(entity_opt) => {
return Command::batch(self.selected_paths(entity_opt).into_iter().filter_map( return Task::batch(self.selected_paths(entity_opt).into_iter().filter_map(
|path| { |path| {
if path.is_dir() { if path.is_dir() {
Some(self.open_tab(Location::Path(path), false, None)) Some(self.open_tab(Location::Path(path), false, None))
@ -2178,7 +2177,7 @@ impl Application for App {
} }
}, },
Message::OpenItemLocation(entity_opt) => { Message::OpenItemLocation(entity_opt) => {
return Command::batch(self.selected_paths(entity_opt).into_iter().filter_map( return Task::batch(self.selected_paths(entity_opt).into_iter().filter_map(
|path| { |path| {
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
Some(self.open_tab( Some(self.open_tab(
@ -2259,7 +2258,7 @@ impl Application for App {
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) { if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
if let Some(path) = tab.location.path_opt() { if let Some(path) = tab.location.path_opt() {
let to = path.clone(); let to = path.clone();
return clipboard::read_data::<ClipboardPaste, _>(move |contents_opt| { return clipboard::read_data::<ClipboardPaste>().map(move |contents_opt| {
match contents_opt { match contents_opt {
Some(contents) => { Some(contents) => {
message::app(Message::PasteContents(to.clone(), contents)) message::app(Message::PasteContents(to.clone(), contents))
@ -2340,7 +2339,7 @@ impl Application for App {
commands.push(self.rescan_trash()); commands.push(self.rescan_trash());
// if search is active, update "search" tab view // if search is active, update "search" tab view
commands.push(self.search()); commands.push(self.search());
return Command::batch(commands); return Task::batch(commands);
} }
Message::PendingError(id, err) => { Message::PendingError(id, err) => {
if let Some((op, _)) = self.pending_operations.remove(&id) { if let Some((op, _)) = self.pending_operations.remove(&id) {
@ -2384,7 +2383,7 @@ impl Application for App {
"com.system76.CosmicFilesDialog".to_string(); "com.system76.CosmicFilesDialog".to_string();
} }
let (id, command) = window::spawn(settings); let (id, command) = window::open(settings);
self.windows.insert( self.windows.insert(
id, id,
WindowKind::Preview( WindowKind::Preview(
@ -2392,9 +2391,9 @@ impl Application for App {
PreviewKind::Location(Location::Path(path)), PreviewKind::Location(Location::Path(path)),
), ),
); );
commands.push(command); commands.push(command.map(|_id| message::none()));
} }
return Command::batch(commands); return Task::batch(commands);
} }
} }
} }
@ -2411,7 +2410,7 @@ impl Application for App {
.icon_set(entity, widget::icon::icon(tab::trash_icon_symbolic(16))); .icon_set(entity, widget::icon::icon(tab::trash_icon_symbolic(16)));
} }
return Command::batch([self.rescan_trash(), self.update_desktop()]); return Task::batch([self.rescan_trash(), self.update_desktop()]);
} }
Message::Rename(entity_opt) => { Message::Rename(entity_opt) => {
@ -2452,7 +2451,7 @@ impl Application for App {
if let Some(dialog_page) = self.dialog_pages.pop_front() { if let Some(dialog_page) = self.dialog_pages.pop_front() {
match dialog_page { match dialog_page {
DialogPage::Replace { tx, .. } => { DialogPage::Replace { tx, .. } => {
return Command::perform( return Task::perform(
async move { async move {
let _ = tx.send(replace_result).await; let _ = tx.send(replace_result).await;
message::none() message::none()
@ -2572,10 +2571,12 @@ impl Application for App {
// If that was the last tab, close window // If that was the last tab, close window
if self.tab_model.iter().next().is_none() { if self.tab_model.iter().next().is_none() {
return window::close(window::Id::MAIN); if let Some(window_id) = &self.window_id_opt {
return window::close(*window_id);
}
} }
return Command::batch([self.update_title(), self.update_watcher()]); return Task::batch([self.update_title(), self.update_watcher()]);
} }
Message::TabConfig(config) => { Message::TabConfig(config) => {
if config != self.config.tab { if config != self.config.tab {
@ -2591,7 +2592,7 @@ impl Application for App {
Message::TabMessage(entity_opt, tab_message) => { Message::TabMessage(entity_opt, tab_message) => {
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active()); let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
//TODO: move to Command? //TODO: move to Task?
if let tab::Message::ContextMenu(_point_opt) = tab_message { if let tab::Message::ContextMenu(_point_opt) = tab_message {
// Disable side context page // Disable side context page
self.set_show_context(false); self.set_show_context(false);
@ -2626,7 +2627,7 @@ impl Application for App {
self.activate_nav_model_location(&tab_path); self.activate_nav_model_location(&tab_path);
self.tab_model.text_set(entity, tab_title); self.tab_model.text_set(entity, tab_title);
commands.push(Command::batch([ commands.push(Task::batch([
self.update_title(), self.update_title(),
self.update_watcher(), self.update_watcher(),
self.rescan_tab(entity, tab_path, selection_path), self.rescan_tab(entity, tab_path, selection_path),
@ -2639,7 +2640,7 @@ impl Application for App {
self.dialog_pages.push_back(DialogPage::EmptyTrash); self.dialog_pages.push_back(DialogPage::EmptyTrash);
} }
tab::Command::Iced(iced_command) => { tab::Command::Iced(iced_command) => {
commands.push(iced_command.map(move |tab_message| { commands.push(iced_command.0.map(move |tab_message| {
message::app(Message::TabMessage(Some(entity), tab_message)) message::app(Message::TabMessage(Some(entity), tab_message))
})); }));
} }
@ -2678,14 +2679,18 @@ impl Application for App {
self.set_context_title(self.context_page.title()); self.set_context_title(self.context_page.title());
} }
tab::Command::WindowDrag => { tab::Command::WindowDrag => {
commands.push(window::drag(self.main_window_id())); if let Some(window_id) = &self.window_id_opt {
commands.push(window::drag(*window_id));
}
} }
tab::Command::WindowToggleMaximize => { tab::Command::WindowToggleMaximize => {
commands.push(window::toggle_maximize(self.main_window_id())); if let Some(window_id) = &self.window_id_opt {
commands.push(window::toggle_maximize(*window_id));
}
} }
} }
} }
return Command::batch(commands); return Task::batch(commands);
} }
Message::TabNew => { Message::TabNew => {
let active = self.tab_model.active(); let active = self.tab_model.active();
@ -2766,9 +2771,9 @@ impl Application for App {
} }
Message::WindowClose => { Message::WindowClose => {
if let Some(window_id) = self.window_id_opt.take() { if let Some(window_id) = self.window_id_opt.take() {
return Command::batch([ return Task::batch([
window::close(window_id), window::close(window_id),
Command::perform(async move { message::app(Message::MaybeExit) }, |x| x), Task::perform(async move { message::app(Message::MaybeExit) }, |x| x),
]); ]);
} }
} }
@ -2849,8 +2854,8 @@ impl Application for App {
if let Some(location) = self.nav_model.data::<Location>(entity) { if let Some(location) = self.nav_model.data::<Location>(entity) {
self.nav_dnd_hover = Some((location.clone(), Instant::now())); self.nav_dnd_hover = Some((location.clone(), Instant::now()));
let location = location.clone(); let location = location.clone();
return Command::perform(tokio::time::sleep(HOVER_DURATION), move |_| { return Task::perform(tokio::time::sleep(HOVER_DURATION), move |_| {
cosmic::app::Message::App(Message::DndHoverLocTimeout(location)) cosmic::app::Message::App(Message::DndHoverLocTimeout(location.clone()))
}); });
} }
} }
@ -2874,11 +2879,11 @@ impl Application for App {
)), )),
Location::Trash if matches!(action, DndAction::Move) => { Location::Trash if matches!(action, DndAction::Move) => {
self.operation(Operation::Delete { paths: data.paths }); self.operation(Operation::Delete { paths: data.paths });
Command::none() Task::none()
} }
_ => { _ => {
log::warn!("Copy to trash is not supported."); log::warn!("Copy to trash is not supported.");
Command::none() Task::none()
} }
}; };
return ret; return ret;
@ -2901,7 +2906,7 @@ impl Application for App {
}; };
if let Some(title) = title_opt { if let Some(title) = title_opt {
self.tab_model.text_set(entity, title); self.tab_model.text_set(entity, title);
return Command::batch([ return Task::batch([
self.update_title(), self.update_title(),
self.update_watcher(), self.update_watcher(),
self.rescan_tab(entity, location, None), self.rescan_tab(entity, location, None),
@ -2911,7 +2916,7 @@ impl Application for App {
} }
Message::DndEnterTab(entity) => { Message::DndEnterTab(entity) => {
self.tab_dnd_hover = Some((entity, Instant::now())); self.tab_dnd_hover = Some((entity, Instant::now()));
return Command::perform(tokio::time::sleep(HOVER_DURATION), move |_| { return Task::perform(tokio::time::sleep(HOVER_DURATION), move |_| {
cosmic::app::Message::App(Message::DndHoverTabTimeout(entity)) cosmic::app::Message::App(Message::DndHoverTabTimeout(entity))
}); });
} }
@ -2935,11 +2940,11 @@ impl Application for App {
)), )),
Location::Trash if matches!(action, DndAction::Move) => { Location::Trash if matches!(action, DndAction::Move) => {
self.operation(Operation::Delete { paths: data.paths }); self.operation(Operation::Delete { paths: data.paths });
Command::none() Task::none()
} }
_ => { _ => {
log::warn!("Copy to trash is not supported."); log::warn!("Copy to trash is not supported.");
Command::none() Task::none()
} }
}; };
return ret; return ret;
@ -3131,7 +3136,7 @@ impl Application for App {
None, None,
); );
self.windows.insert(surface_id, WindowKind::Desktop(entity)); self.windows.insert(surface_id, WindowKind::Desktop(entity));
return Command::batch([ return Task::batch([
command, command,
get_layer_surface(SctkLayerSurfaceSettings { get_layer_surface(SctkLayerSurfaceSettings {
id: surface_id, id: surface_id,
@ -3173,12 +3178,12 @@ impl Application for App {
} }
Message::Cosmic(cosmic) => { Message::Cosmic(cosmic) => {
// Forward cosmic messages // Forward cosmic messages
return Command::perform(async move { cosmic }, |cosmic| message::cosmic(cosmic)); return Task::perform(async move { cosmic }, |cosmic| message::cosmic(cosmic));
} }
Message::None => {} Message::None => {}
} }
Command::none() Task::none()
} }
fn context_drawer(&self) -> Option<Element<Message>> { fn context_drawer(&self) -> Option<Element<Message>> {
@ -3288,7 +3293,7 @@ impl Application for App {
}) })
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into(), .into(),
]) ])
@ -3396,18 +3401,19 @@ 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.push( controls.push(
widget::checkbox(fl!("remember-password"), *remember, move |value| { widget::checkbox(fl!("remember-password"), *remember)
Message::DialogUpdate(DialogPage::NetworkAuth { .on_toggle(move |value| {
mounter_key: *mounter_key, Message::DialogUpdate(DialogPage::NetworkAuth {
uri: uri.clone(), mounter_key: *mounter_key,
auth: MounterAuth { uri: uri.clone(),
remember_opt: Some(value), auth: MounterAuth {
..auth.clone() remember_opt: Some(value),
}, ..auth.clone()
auth_tx: auth_tx.clone(), },
auth_tx: auth_tx.clone(),
})
}) })
}) .into(),
.into(),
); );
} }
@ -3540,21 +3546,21 @@ impl Application for App {
} else { } else {
widget::text(app.name.to_string()).into() widget::text(app.name.to_string()).into()
}, },
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
if *selected == i { if *selected == i {
widget::icon::from_name("checkbox-checked-symbolic") widget::icon::from_name("checkbox-checked-symbolic")
.size(16) .size(16)
.into() .into()
} else { } else {
widget::horizontal_space(Length::Fixed(16.0)).into() widget::Space::with_width(Length::Fixed(16.0)).into()
}, },
]) ])
.spacing(space_s) .spacing(space_s)
.height(Length::Fixed(32.0)) .height(Length::Fixed(32.0))
.align_items(Alignment::Center), .align_y(Alignment::Center),
) )
.width(Length::Fill) .width(Length::Fill)
.style(theme::Button::MenuItem) .class(theme::Button::MenuItem)
.on_press(Message::OpenWithSelection(i)), .on_press(Message::OpenWithSelection(i)),
); );
} }
@ -3668,19 +3674,19 @@ impl Application for App {
)); ));
if *multiple { if *multiple {
dialog dialog
.control(widget::checkbox( .control(
fl!("apply-to-all"), widget::checkbox(fl!("apply-to-all"), *apply_to_all).on_toggle(
*apply_to_all, |apply_to_all| {
|apply_to_all| { Message::DialogUpdate(DialogPage::Replace {
Message::DialogUpdate(DialogPage::Replace { from: from.clone(),
from: from.clone(), to: to.clone(),
to: to.clone(), multiple: *multiple,
multiple: *multiple, apply_to_all,
apply_to_all, tx: tx.clone(),
tx: tx.clone(), })
}) },
}, ),
)) )
.secondary_action( .secondary_action(
widget::button::standard(fl!("skip")).on_press(Message::ReplaceResult( widget::button::standard(fl!("skip")).on_press(Message::ReplaceResult(
ReplaceResult::Skip(*apply_to_all), ReplaceResult::Skip(*apply_to_all),
@ -3710,12 +3716,12 @@ impl Application for App {
widget::dialog(fl!("set-executable-and-launch")) widget::dialog(fl!("set-executable-and-launch"))
.primary_action( .primary_action(
widget::button::text(fl!("set-and-launch")) widget::button::text(fl!("set-and-launch"))
.style(theme::Button::Suggested) .class(theme::Button::Suggested)
.on_press(Message::DialogComplete), .on_press(Message::DialogComplete),
) )
.secondary_action( .secondary_action(
widget::button::text(fl!("cancel")) widget::button::text(fl!("cancel"))
.style(theme::Button::Standard) .class(theme::Button::Standard)
.on_press(Message::DialogCancel), .on_press(Message::DialogCancel),
) )
.control(widget::text::text(fl!( .control(widget::text::text(fl!(
@ -3818,7 +3824,7 @@ impl Application for App {
}) })
.drag_id(self.tab_drag_id), .drag_id(self.tab_drag_id),
) )
.style(style::Container::Background) .class(style::Container::Background)
.width(Length::Fill) .width(Length::Fill)
.padding([0, space_s]), .padding([0, space_s]),
); );
@ -3838,10 +3844,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( tab_column = tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space()));
&self.toasts,
widget::horizontal_space(Length::Fill),
));
let content: Element<_> = tab_column.into(); let content: Element<_> = tab_column.into();
@ -3859,7 +3862,7 @@ impl Application for App {
Some(tab) => tab Some(tab) => tab
.view(&self.key_binds) .view(&self.key_binds)
.map(move |message| Message::TabMessage(Some(*entity), message)), .map(move |message| Message::TabMessage(Some(*entity), message)),
None => widget::vertical_space(Length::Fill).into(), None => widget::vertical_space().into(),
}; };
let mut popover = widget::popover(tab_view); let mut popover = widget::popover(tab_view);
@ -3869,10 +3872,8 @@ impl Application for App {
tab_column = tab_column.push(popover); tab_column = tab_column.push(popover);
// 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( tab_column =
&self.toasts, tab_column.push(widget::toaster(&self.toasts, widget::horizontal_space()));
widget::horizontal_space(Length::Fill),
));
return tab_column.into(); return tab_column.into();
} }
@ -3895,13 +3896,13 @@ impl Application for App {
widget::container( widget::container(
widget::scrollable(widget::row::with_children(vec![ widget::scrollable(widget::row::with_children(vec![
content, content,
widget::horizontal_space(Length::Fixed( widget::Space::with_width(Length::Fixed(
(scrollbar_width + scrollbar_margin).into(), (scrollbar_width + scrollbar_margin).into(),
)) ))
.into(), .into(),
])) ]))
.direction(scrollable::Direction::Vertical( .direction(scrollable::Direction::Vertical(
scrollable::Properties::new() scrollable::Scrollbar::new()
.width(scrollbar_width) .width(scrollbar_width)
.scroller_width(scrollbar_width), .scroller_width(scrollbar_width),
)), )),
@ -3914,7 +3915,7 @@ impl Application for App {
space_l, space_l,
space_l, space_l,
]) ])
.style(theme::Container::WindowBackground) .class(theme::Container::WindowBackground)
.into() .into()
} }
@ -3924,7 +3925,7 @@ impl Application for App {
struct TrashWatcherSubscription; struct TrashWatcherSubscription;
let mut subscriptions = vec![ let mut subscriptions = vec![
event::listen_with(|event, status| match event { event::listen_with(|event, status, _window_id| match event {
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status { Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status {
event::Status::Ignored => Some(Message::Key(modifiers, key)), event::Status::Ignored => Some(Message::Key(modifiers, key)),
event::Status::Captured => None, event::Status::Captured => None,
@ -3932,7 +3933,7 @@ impl Application for App {
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => { Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
Some(Message::Modifiers(modifiers)) Some(Message::Modifiers(modifiers))
} }
Event::Window(_id, WindowEvent::CloseRequested) => Some(Message::WindowClose), Event::Window(WindowEvent::CloseRequested) => Some(Message::WindowClose),
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
Event::PlatformSpecific(event::PlatformSpecific::Wayland(wayland_event)) => { Event::PlatformSpecific(event::PlatformSpecific::Wayland(wayland_event)) => {
match wayland_event { match wayland_event {
@ -3969,10 +3970,9 @@ impl Application for App {
} }
Message::SystemThemeModeChange(update.config) Message::SystemThemeModeChange(update.config)
}), }),
subscription::channel( Subscription::run_with_id(
TypeId::of::<WatcherSubscription>(), TypeId::of::<WatcherSubscription>(),
100, stream::channel(100, |mut output| async move {
|mut output| async move {
let watcher_res = { let watcher_res = {
let mut output = output.clone(); let mut output = output.clone();
new_debouncer( new_debouncer(
@ -4044,12 +4044,11 @@ impl Application for App {
} }
std::future::pending().await std::future::pending().await
}, }),
), ),
subscription::channel( Subscription::run_with_id(
TypeId::of::<TrashWatcherSubscription>(), TypeId::of::<TrashWatcherSubscription>(),
25, stream::channel(25, |mut output| async move {
|mut output| 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)),
@ -4110,7 +4109,7 @@ impl Application for App {
} }
std::future::pending().await std::future::pending().await
}, }),
), ),
]; ];
@ -4137,10 +4136,9 @@ impl Application for App {
#[cfg(feature = "notify")] #[cfg(feature = "notify")]
{ {
struct NotificationSubscription; struct NotificationSubscription;
subscriptions.push(subscription::channel( subscriptions.push(Subscription::run_with_id(
TypeId::of::<NotificationSubscription>(), TypeId::of::<NotificationSubscription>(),
1, stream::channel(1, move |msg_tx| async move {
move |msg_tx| 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()
@ -4168,7 +4166,7 @@ impl Application for App {
.unwrap(); .unwrap();
std::future::pending().await std::future::pending().await
}, }),
)); ));
} }
} }
@ -4178,23 +4176,26 @@ impl Application for App {
//TODO: use recipe? //TODO: use recipe?
let id = *id; let id = *id;
let pending_operation = pending_operation.clone(); let pending_operation = pending_operation.clone();
subscriptions.push(subscription::channel(id, 16, move |msg_tx| async move { subscriptions.push(Subscription::run_with_id(
let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx)); id,
match pending_operation.perform(id, &msg_tx).await { stream::channel(16, move |msg_tx| async move {
Ok(()) => { let msg_tx = Arc::new(tokio::sync::Mutex::new(msg_tx));
let _ = msg_tx.lock().await.send(Message::PendingComplete(id)).await; match pending_operation.perform(id, &msg_tx).await {
Ok(()) => {
let _ = msg_tx.lock().await.send(Message::PendingComplete(id)).await;
}
Err(err) => {
let _ = msg_tx
.lock()
.await
.send(Message::PendingError(id, err.to_string()))
.await;
}
} }
Err(err) => {
let _ = msg_tx
.lock()
.await
.send(Message::PendingError(id, err.to_string()))
.await;
}
}
std::future::pending().await std::future::pending().await
})); }),
));
} }
for entity in self.tab_model.iter() { for entity in self.tab_model.iter() {

View file

@ -4,7 +4,7 @@ use std::{any::TypeId, num::NonZeroU16, path::PathBuf};
use cosmic::{ use cosmic::{
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry}, cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
iced::subscription::Subscription, iced::Subscription,
theme, Application, theme, Application,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View file

@ -1,19 +1,14 @@
// Copyright 2023 System76 <info@system76.com> // Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only // SPDX-License-Identifier: GPL-3.0-only
#[cfg(feature = "winit")]
use cosmic::iced::multi_window::Application as IcedApplication;
#[cfg(feature = "wayland")]
use cosmic::iced::Application as IcedApplication;
use cosmic::{ use cosmic::{
app::{self, cosmic::Cosmic, message, Command, Core}, app::{self, cosmic::Cosmic, message, Core, Task},
cosmic_config, cosmic_theme, executor, cosmic_config, cosmic_theme, executor,
iced::{ iced::{
event, event,
futures::{self, SinkExt}, futures::{self, SinkExt},
keyboard::{Event as KeyEvent, Key, Modifiers}, keyboard::{Event as KeyEvent, Key, Modifiers},
subscription::{self, Subscription}, stream, window, Alignment, Event, Length, Size, Subscription,
window, Alignment, Event, Length, Size,
}, },
theme, theme,
widget::{ widget::{
@ -156,7 +151,7 @@ impl<M: Send + 'static> Dialog<M> {
path_opt: Option<PathBuf>, path_opt: Option<PathBuf>,
mapper: fn(DialogMessage) -> M, mapper: fn(DialogMessage) -> M,
on_result: impl Fn(DialogResult) -> M + 'static, on_result: impl Fn(DialogResult) -> M + 'static,
) -> (Self, Command<M>) { ) -> (Self, Task<M>) {
//TODO: only do this once somehow? //TODO: only do this once somehow?
crate::localize::localize(); crate::localize::localize();
@ -175,9 +170,10 @@ impl<M: Send + 'static> Dialog<M> {
settings.platform_specific.application_id = App::APP_ID.to_string(); settings.platform_specific.application_id = App::APP_ID.to_string();
} }
let (window_id, window_command) = window::spawn(settings); let (window_id, window_command) = window::open(settings.clone());
let core = Core::default(); let mut core = Core::default();
core.set_main_window_id(Some(window_id));
let flags = Flags { let flags = Flags {
kind, kind,
path_opt: path_opt path_opt: path_opt
@ -193,21 +189,24 @@ impl<M: Send + 'static> Dialog<M> {
config_handler, config_handler,
config, config,
}; };
let (cosmic, cosmic_command) = <Cosmic<App> as IcedApplication>::new((core, flags));
let (cosmic, cosmic_command) = Cosmic::<App>::init((core, flags));
( (
Self { Self {
cosmic, cosmic,
mapper, mapper,
on_result: Box::new(on_result), on_result: Box::new(on_result),
}, },
Command::batch([window_command, cosmic_command]) Task::batch([
.map(DialogMessage) window_command.map(|_id| message::none()),
.map(move |message| app::Message::App(mapper(message))), cosmic_command
.map(DialogMessage)
.map(move |message| app::Message::App(mapper(message))),
]),
) )
} }
pub fn set_title(&mut self, title: impl Into<String>) -> Command<M> { pub fn set_title(&mut self, title: impl Into<String>) -> Task<M> {
let mapper = self.mapper; let mapper = self.mapper;
self.cosmic.app.title = title.into(); self.cosmic.app.title = title.into();
self.cosmic self.cosmic
@ -237,7 +236,7 @@ impl<M: Send + 'static> Dialog<M> {
&mut self, &mut self,
filters: impl Into<Vec<DialogFilter>>, filters: impl Into<Vec<DialogFilter>>,
filter_selected: Option<usize>, filter_selected: Option<usize>,
) -> Command<M> { ) -> Task<M> {
let mapper = self.mapper; let mapper = self.mapper;
self.cosmic.app.filters = filters.into(); self.cosmic.app.filters = filters.into();
self.cosmic.app.filter_selected = filter_selected; self.cosmic.app.filter_selected = filter_selected;
@ -255,7 +254,7 @@ impl<M: Send + 'static> Dialog<M> {
.map(self.mapper) .map(self.mapper)
} }
pub fn update(&mut self, message: DialogMessage) -> Command<M> { pub fn update(&mut self, message: DialogMessage) -> Task<M> {
let mapper = self.mapper; let mapper = self.mapper;
let command = self let command = self
.cosmic .cosmic
@ -264,9 +263,9 @@ impl<M: Send + 'static> Dialog<M> {
.map(move |message| app::Message::App(mapper(message))); .map(move |message| app::Message::App(mapper(message)));
if let Some(result) = self.cosmic.app.result_opt.take() { if let Some(result) = self.cosmic.app.result_opt.take() {
let on_result_message = (self.on_result)(result); let on_result_message = (self.on_result)(result);
Command::batch([ Task::batch([
command, command,
Command::perform(async move { app::Message::App(on_result_message) }, |x| x), Task::perform(async move { app::Message::App(on_result_message) }, |x| x),
]) ])
} else { } else {
command command
@ -281,7 +280,7 @@ impl<M: Send + 'static> Dialog<M> {
} }
pub fn window_id(&self) -> window::Id { pub fn window_id(&self) -> window::Id {
self.cosmic.app.main_window_id() self.cosmic.app.flags.window_id
} }
} }
@ -421,7 +420,7 @@ impl App {
let mut row = widget::row::with_capacity( let mut row = widget::row::with_capacity(
if !self.filters.is_empty() { 1 } else { 0 } + self.choices.len() * 2 + 3, if !self.filters.is_empty() { 1 } else { 0 } + self.choices.len() * 2 + 3,
) )
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs); .spacing(space_xxs);
if !self.filters.is_empty() { if !self.filters.is_empty() {
row = row.push(widget::dropdown( row = row.push(widget::dropdown(
@ -433,7 +432,7 @@ 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.push(widget::checkbox(label, *value, move |checked| { row = row.push(widget::checkbox(label, *value).on_toggle(move |checked| {
Message::Choice(choice_i, if checked { 1 } else { 0 }) Message::Choice(choice_i, if checked { 1 } else { 0 })
})); }));
} }
@ -450,7 +449,7 @@ impl App {
} }
} }
} }
row = row.push(widget::horizontal_space(Length::Fill)); row = row.push(widget::horizontal_space());
row = row.push(widget::button::standard(fl!("cancel")).on_press(Message::Cancel)); row = row.push(widget::button::standard(fl!("cancel")).on_press(Message::Cancel));
row = row.push(if self.flags.kind.save() { row = row.push(if self.flags.kind.save() {
widget::button::suggested(&self.accept_label).on_press(Message::Save(false)) widget::button::suggested(&self.accept_label).on_press(Message::Save(false))
@ -505,10 +504,10 @@ impl App {
widget::settings::view_column(children).into() widget::settings::view_column(children).into()
} }
fn rescan_tab(&self) -> Command<Message> { fn rescan_tab(&self) -> Task<Message> {
let location = self.tab.location.clone(); let location = self.tab.location.clone();
let icon_sizes = self.tab.config.icon_sizes; let icon_sizes = self.tab.config.icon_sizes;
Command::perform( Task::perform(
async move { async move {
let location2 = location.clone(); let location2 = location.clone();
match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await { match tokio::task::spawn_blocking(move || location2.scan(icon_sizes)).await {
@ -532,7 +531,7 @@ impl App {
} }
} }
fn search_set(&mut self, term_opt: Option<String>) -> Command<Message> { fn search_set(&mut self, term_opt: Option<String>) -> Task<Message> {
let location_opt = match term_opt { let location_opt = match term_opt {
Some(term) => match &self.tab.location { Some(term) => match &self.tab.location {
Location::Path(path) | Location::Search(path, ..) => Some(( Location::Path(path) | Location::Search(path, ..) => Some((
@ -553,23 +552,23 @@ impl App {
}; };
if let Some((location, focus_search)) = location_opt { if let Some((location, focus_search)) = location_opt {
self.tab.change_location(&location, None); self.tab.change_location(&location, None);
return Command::batch([ return Task::batch([
self.update_title(), self.update_title(),
self.update_watcher(), self.update_watcher(),
self.rescan_tab(), self.rescan_tab(),
if focus_search { if focus_search {
widget::text_input::focus(self.search_id.clone()) widget::text_input::focus(self.search_id.clone())
} else { } else {
Command::none() Task::none()
}, },
]); ]);
} }
Command::none() Task::none()
} }
fn update_config(&mut self) -> Command<Message> { fn update_config(&mut self) -> Task<Message> {
self.update_nav_model(); self.update_nav_model();
Command::none() Task::none()
} }
fn activate_nav_model_location(&mut self, location: &Location) { fn activate_nav_model_location(&mut self, location: &Location) {
@ -657,12 +656,12 @@ impl App {
self.activate_nav_model_location(&self.tab.location.clone()); self.activate_nav_model_location(&self.tab.location.clone());
} }
fn update_title(&mut self) -> Command<Message> { fn update_title(&mut self) -> Task<Message> {
self.set_header_title(self.title.clone()); self.set_header_title(self.title.clone());
self.set_window_title(self.title.clone(), self.main_window_id()) self.set_window_title(self.title.clone(), self.flags.window_id)
} }
fn update_watcher(&mut self) -> Command<Message> { fn update_watcher(&mut self) -> Task<Message> {
if let Some((mut watcher, old_paths)) = self.watcher_opt.take() { if let Some((mut watcher, old_paths)) = self.watcher_opt.take() {
let mut new_paths = HashSet::new(); let mut new_paths = HashSet::new();
if let Some(path) = &self.tab.location.path_opt() { if let Some(path) = &self.tab.location.path_opt() {
@ -705,7 +704,7 @@ impl App {
} }
//TODO: should any of this run in a command? //TODO: should any of this run in a command?
Command::none() Task::none()
} }
} }
@ -732,7 +731,7 @@ impl Application for App {
} }
/// Creates the application, and optionally emits command on initialize. /// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Command<Message>) { fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Message>) {
core.window.context_is_overlay = false; core.window.context_is_overlay = false;
core.window.show_close = false; core.window.show_close = false;
core.window.show_maximize = false; core.window.show_maximize = false;
@ -785,7 +784,7 @@ impl Application for App {
watcher_opt: None, watcher_opt: None,
}; };
let commands = Command::batch([ let commands = Task::batch([
app.update_config(), app.update_config(),
app.update_title(), app.update_title(),
app.update_watcher(), app.update_watcher(),
@ -795,10 +794,6 @@ impl Application for App {
(app, commands) (app, commands)
} }
fn main_window_id(&self) -> window::Id {
self.flags.window_id
}
fn context_drawer(&self) -> Option<Element<Message>> { fn context_drawer(&self) -> Option<Element<Message>> {
if !self.core.window.show_context { if !self.core.window.show_context {
return None; return None;
@ -822,7 +817,7 @@ impl Application for App {
widget::container(self.button_view()) widget::container(self.button_view())
.width(Length::Fill) .width(Length::Fill)
.padding(space_xxs) .padding(space_xxs)
.style(theme::Container::WindowBackground) .class(theme::Container::WindowBackground)
.into(), .into(),
]) ])
.into(), .into(),
@ -1004,7 +999,7 @@ impl Application for App {
None None
} }
fn on_nav_select(&mut self, entity: segmented_button::Entity) -> Command<Message> { fn on_nav_select(&mut self, entity: segmented_button::Entity) -> Task<Message> {
self.nav_model.activate(entity); self.nav_model.activate(entity);
if let Some(location) = self.nav_model.data::<Location>(entity) { if let Some(location) = self.nav_model.data::<Location>(entity) {
let message = Message::TabMessage(tab::Message::Location(location.clone())); let message = Message::TabMessage(tab::Message::Location(location.clone()));
@ -1016,14 +1011,14 @@ impl Application for App {
return mounter.mount(data.1.clone()).map(|_| message::none()); return mounter.mount(data.1.clone()).map(|_| message::none());
} }
} }
Command::none() Task::none()
} }
fn on_escape(&mut self) -> Command<Message> { fn on_escape(&mut self) -> Task<Message> {
if self.tab.gallery { if self.tab.gallery {
// Close gallery if open // Close gallery if open
self.tab.gallery = false; self.tab.gallery = false;
return Command::none(); return Task::none();
} }
if self.search_get().is_some() { if self.search_get().is_some() {
@ -1033,13 +1028,13 @@ impl Application for App {
if self.tab.context_menu.is_some() { if self.tab.context_menu.is_some() {
self.tab.context_menu = None; self.tab.context_menu = None;
return Command::none(); return Task::none();
} }
if self.tab.edit_location.is_some() { if self.tab.edit_location.is_some() {
// Close location editing if enabled // Close location editing if enabled
self.tab.edit_location = None; self.tab.edit_location = None;
return Command::none(); return Task::none();
} }
let had_focused_button = self.tab.select_focus_id().is_some(); let had_focused_button = self.tab.select_focus_id().is_some();
@ -1048,19 +1043,19 @@ impl Application for App {
// Unfocus if there was a focused button // Unfocus if there was a focused button
return widget::button::focus(widget::Id::unique()); return widget::button::focus(widget::Id::unique());
} }
return Command::none(); return Task::none();
} }
self.update(Message::Cancel) self.update(Message::Cancel)
} }
/// Handle application events here. /// Handle application events here.
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::None => {} Message::None => {}
Message::Cancel => { Message::Cancel => {
self.result_opt = Some(DialogResult::Cancel); self.result_opt = Some(DialogResult::Cancel);
return window::close(self.main_window_id()); return window::close(self.flags.window_id);
} }
Message::Choice(choice_i, option_i) => { Message::Choice(choice_i, option_i) => {
if let Some(choice) = self.choices.get_mut(choice_i) { if let Some(choice) = self.choices.get_mut(choice_i) {
@ -1187,7 +1182,7 @@ impl Application for App {
//TODO: this could change favorites IDs while they are in use //TODO: this could change favorites IDs while they are in use
self.update_nav_model(); self.update_nav_model();
return Command::batch(commands); return Task::batch(commands);
} }
Message::NewFolder => { Message::NewFolder => {
if let Some(path) = self.tab.location.path_opt() { if let Some(path) = self.tab.location.path_opt() {
@ -1293,7 +1288,7 @@ impl Application for App {
return self.update(message); return self.update(message);
} else { } else {
// Otherwise, this is not a legal selection // Otherwise, this is not a legal selection
return Command::none(); return Task::none();
} }
} }
} }
@ -1301,7 +1296,7 @@ impl Application for App {
// If there are proper matching items, return them // If there are proper matching items, return them
if !paths.is_empty() { if !paths.is_empty() {
self.result_opt = Some(DialogResult::Open(paths)); self.result_opt = Some(DialogResult::Open(paths));
return window::close(self.main_window_id()); return window::close(self.flags.window_id);
} }
// If we are in directory mode, return the current directory // If we are in directory mode, return the current directory
@ -1309,7 +1304,7 @@ impl Application for App {
match &self.tab.location { match &self.tab.location {
Location::Path(tab_path) => { Location::Path(tab_path) => {
self.result_opt = Some(DialogResult::Open(vec![tab_path.clone()])); self.result_opt = Some(DialogResult::Open(vec![tab_path.clone()]));
return window::close(self.main_window_id()); return window::close(self.flags.window_id);
} }
_ => {} _ => {}
} }
@ -1341,7 +1336,7 @@ impl Application for App {
}); });
} else { } else {
self.result_opt = Some(DialogResult::Open(vec![path])); self.result_opt = Some(DialogResult::Open(vec![path]));
return window::close(self.main_window_id()); return window::close(self.flags.window_id);
} }
} }
} }
@ -1388,12 +1383,11 @@ impl Application for App {
commands.push(self.update(Message::from(action.message()))); commands.push(self.update(Message::from(action.message())));
} }
tab::Command::ChangeLocation(_tab_title, _tab_path, _selection_path) => { tab::Command::ChangeLocation(_tab_title, _tab_path, _selection_path) => {
commands commands.push(Task::batch([self.update_watcher(), self.rescan_tab()]));
.push(Command::batch([self.update_watcher(), self.rescan_tab()]));
} }
tab::Command::Iced(iced_command) => { tab::Command::Iced(iced_command) => {
commands.push( commands.push(
iced_command.map(|tab_message| { iced_command.0.map(|tab_message| {
message::app(Message::TabMessage(tab_message)) message::app(Message::TabMessage(tab_message))
}), }),
); );
@ -1411,17 +1405,17 @@ impl Application for App {
self.set_context_title(self.context_page.title()); self.set_context_title(self.context_page.title());
} }
tab::Command::WindowDrag => { tab::Command::WindowDrag => {
commands.push(window::drag(self.main_window_id())); commands.push(window::drag(self.flags.window_id));
} }
tab::Command::WindowToggleMaximize => { tab::Command::WindowToggleMaximize => {
commands.push(window::toggle_maximize(self.main_window_id())); commands.push(window::toggle_maximize(self.flags.window_id));
} }
unsupported => { unsupported => {
log::warn!("{unsupported:?} not supported in dialog mode"); log::warn!("{unsupported:?} not supported in dialog mode");
} }
} }
} }
return Command::batch(commands); return Task::batch(commands);
} }
Message::TabRescan(location, parent_item_opt, mut items) => { Message::TabRescan(location, parent_item_opt, mut items) => {
if location == self.tab.location { if location == self.tab.location {
@ -1554,7 +1548,7 @@ impl Application for App {
} }
} }
Command::none() Task::none()
} }
/// Creates a view after each update. /// Creates a view after each update.
@ -1590,7 +1584,7 @@ impl Application for App {
fn subscription(&self) -> Subscription<Message> { fn subscription(&self) -> Subscription<Message> {
struct WatcherSubscription; struct WatcherSubscription;
let mut subscriptions = vec![ let mut subscriptions = vec![
event::listen_with(|event, status| match event { event::listen_with(|event, status, _window_id| match event {
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status { Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status {
event::Status::Ignored => Some(Message::Key(modifiers, key)), event::Status::Ignored => Some(Message::Key(modifiers, key)),
event::Status::Captured => None, event::Status::Captured => None,
@ -1610,10 +1604,9 @@ impl Application for App {
} }
Message::Config(update.config) Message::Config(update.config)
}), }),
subscription::channel( Subscription::run_with_id(
TypeId::of::<WatcherSubscription>(), TypeId::of::<WatcherSubscription>(),
100, stream::channel(100, |mut output| async move {
|mut output| async move {
let watcher_res = { let watcher_res = {
let mut output = output.clone(); let mut output = output.clone();
new_debouncer( new_debouncer(
@ -1683,7 +1676,7 @@ impl Application for App {
} }
std::future::pending().await std::future::pending().await
}, }),
), ),
self.tab.subscription().map(Message::TabMessage), self.tab.subscription().map(Message::TabMessage),
]; ];

View file

@ -27,11 +27,11 @@ macro_rules! menu_button {
vec![$(Element::from($x)),+] vec![$(Element::from($x)),+]
) )
.height(Length::Fixed(24.0)) .height(Length::Fixed(24.0))
.align_items(Alignment::Center) .align_y(Alignment::Center)
) )
.padding([theme::active().cosmic().spacing.space_xxxs, 16]) .padding([theme::active().cosmic().spacing.space_xxxs, 16])
.width(Length::Fill) .width(Length::Fill)
.style(theme::Button::MenuItem) .class(theme::Button::MenuItem)
); );
} }
@ -62,12 +62,8 @@ pub fn context_menu<'a>(
let menu_item = |label, action| { let menu_item = |label, action| {
let key = find_key(&action); let key = find_key(&action);
menu_button!( menu_button!(text::body(label), horizontal_space(), text::body(key))
text::body(label), .on_press(tab::Message::ContextAction(action))
horizontal_space(Length::Fill),
text::body(key)
)
.on_press(tab::Message::ContextAction(action))
}; };
let (sort_name, sort_direction, _) = tab.sort_options(); let (sort_name, sort_direction, _) = tab.sort_options();
@ -291,10 +287,10 @@ pub fn context_menu<'a>(
container(column::with_children(children)) container(column::with_children(children))
.padding(1) .padding(1)
//TODO: move style to libcosmic //TODO: move style to libcosmic
.style(theme::Container::custom(|theme| { .style(|theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
let component = &cosmic.background.component; let component = &cosmic.background.component;
container::Appearance { container::Style {
icon_color: Some(component.on.into()), icon_color: Some(component.on.into()),
text_color: Some(component.on.into()), text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())), background: Some(Background::Color(component.base.into())),
@ -305,7 +301,7 @@ pub fn context_menu<'a>(
}, },
..Default::default() ..Default::default()
} }
})) })
.width(Length::Fixed(260.0)) .width(Length::Fixed(260.0))
.into() .into()
} }
@ -641,10 +637,10 @@ pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Mess
container(column::with_children(children)) container(column::with_children(children))
.padding(1) .padding(1)
.style(theme::Container::custom(|theme| { .style(|theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
let component = &cosmic.background.component; let component = &cosmic.background.component;
container::Appearance { container::Style {
icon_color: Some(component.on.into()), icon_color: Some(component.on.into()),
text_color: Some(component.on.into()), text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())), background: Some(Background::Color(component.base.into())),
@ -655,7 +651,7 @@ pub fn location_context_menu<'a>(ancestor_index: usize) -> Element<'a, tab::Mess
}, },
..Default::default() ..Default::default()
} }
})) })
.width(Length::Fixed(240.0)) .width(Length::Fixed(240.0))
.into() .into()
} }

View file

@ -1,6 +1,6 @@
use cosmic::{ use cosmic::{
iced::{futures::SinkExt, subscription}, iced::{futures::SinkExt, stream, Subscription},
widget, Command, widget, Task,
}; };
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, path::PathBuf, sync::Arc};
@ -460,9 +460,9 @@ impl Mounter for Gvfs {
items_rx.blocking_recv() items_rx.blocking_recv()
} }
fn mount(&self, item: MounterItem) -> Command<()> { fn mount(&self, item: MounterItem) -> Task<()> {
let command_tx = self.command_tx.clone(); let command_tx = self.command_tx.clone();
Command::perform( Task::perform(
async move { async move {
command_tx.send(Cmd::Mount(item)).unwrap(); command_tx.send(Cmd::Mount(item)).unwrap();
() ()
@ -471,9 +471,9 @@ impl Mounter for Gvfs {
) )
} }
fn network_drive(&self, uri: String) -> Command<()> { fn network_drive(&self, uri: String) -> Task<()> {
let command_tx = self.command_tx.clone(); let command_tx = self.command_tx.clone();
Command::perform( Task::perform(
async move { async move {
command_tx.send(Cmd::NetworkDrive(uri)).unwrap(); command_tx.send(Cmd::NetworkDrive(uri)).unwrap();
() ()
@ -490,9 +490,9 @@ impl Mounter for Gvfs {
items_rx.blocking_recv() items_rx.blocking_recv()
} }
fn unmount(&self, item: MounterItem) -> Command<()> { fn unmount(&self, item: MounterItem) -> Task<()> {
let command_tx = self.command_tx.clone(); let command_tx = self.command_tx.clone();
Command::perform( Task::perform(
async move { async move {
command_tx.send(Cmd::Unmount(item)).unwrap(); command_tx.send(Cmd::Unmount(item)).unwrap();
() ()
@ -501,30 +501,35 @@ impl Mounter for Gvfs {
) )
} }
fn subscription(&self) -> subscription::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::channel(TypeId::of::<Self>(), 1, |mut output| async move { Subscription::run_with_id(
command_tx.send(Cmd::Rescan).unwrap(); TypeId::of::<Self>(),
while let Some(event) = event_rx.lock().await.recv().await { stream::channel(1, |mut output| async move {
match event { command_tx.send(Cmd::Rescan).unwrap();
Event::Changed => command_tx.send(Cmd::Rescan).unwrap(), while let Some(event) = event_rx.lock().await.recv().await {
Event::Items(items) => output.send(MounterMessage::Items(items)).await.unwrap(), match event {
Event::MountResult(item, res) => output Event::Changed => command_tx.send(Cmd::Rescan).unwrap(),
.send(MounterMessage::MountResult(item, res)) Event::Items(items) => {
.await output.send(MounterMessage::Items(items)).await.unwrap()
.unwrap(), }
Event::NetworkAuth(uri, auth, auth_tx) => output Event::MountResult(item, res) => output
.send(MounterMessage::NetworkAuth(uri, auth, auth_tx)) .send(MounterMessage::MountResult(item, res))
.await .await
.unwrap(), .unwrap(),
Event::NetworkResult(uri, res) => output Event::NetworkAuth(uri, auth, auth_tx) => output
.send(MounterMessage::NetworkResult(uri, res)) .send(MounterMessage::NetworkAuth(uri, auth, auth_tx))
.await .await
.unwrap(), .unwrap(),
Event::NetworkResult(uri, res) => output
.send(MounterMessage::NetworkResult(uri, res))
.await
.unwrap(),
}
} }
} pending().await
pending().await }),
}) )
} }
} }

View file

@ -1,4 +1,4 @@
use cosmic::{iced::subscription, widget, Command}; use cosmic::{iced::Subscription, widget, Task};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::{collections::BTreeMap, fmt, path::PathBuf, sync::Arc}; use std::{collections::BTreeMap, fmt, path::PathBuf, sync::Arc};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@ -93,11 +93,11 @@ pub enum MounterMessage {
pub trait Mounter: Send + Sync { pub trait Mounter: Send + Sync {
fn items(&self, sizes: IconSizes) -> Option<MounterItems>; fn items(&self, sizes: IconSizes) -> Option<MounterItems>;
//TODO: send result //TODO: send result
fn mount(&self, item: MounterItem) -> Command<()>; fn mount(&self, item: MounterItem) -> Task<()>;
fn network_drive(&self, uri: String) -> Command<()>; fn network_drive(&self, uri: String) -> Task<()>;
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>; fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
fn unmount(&self, item: MounterItem) -> Command<()>; fn unmount(&self, item: MounterItem) -> Task<()>;
fn subscription(&self) -> subscription::Subscription<MounterMessage>; fn subscription(&self) -> Subscription<MounterMessage>;
} }
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]

View file

@ -17,8 +17,8 @@ use cosmic::{
overlay, overlay,
renderer::{self, Quad, Renderer as _}, renderer::{self, Quad, Renderer as _},
touch, touch,
widget::{tree, Operation, OperationOutputWrapper, Tree}, widget::{tree, Operation, Tree},
Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Widget, Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget,
}, },
widget::Id, widget::Id,
Element, Renderer, Theme, Element, Renderer, Theme,
@ -218,15 +218,21 @@ impl State {
let new = if let Some((prev_click, prev_time)) = self.prev_click.take() { let new = if let Some((prev_click, prev_time)) = self.prev_click.take() {
if now.duration_since(prev_time) < DOUBLE_CLICK_DURATION { if now.duration_since(prev_time) < DOUBLE_CLICK_DURATION {
match prev_click.kind() { match prev_click.kind() {
mouse::click::Kind::Single => mouse::Click::new(pos, Some(prev_click)), mouse::click::Kind::Single => {
mouse::click::Kind::Double => mouse::Click::new(pos, Some(prev_click)), mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
mouse::click::Kind::Triple => mouse::Click::new(pos, Some(prev_click)), }
mouse::click::Kind::Double => {
mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
}
mouse::click::Kind::Triple => {
mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
}
} }
} else { } else {
mouse::Click::new(pos, None) mouse::Click::new(pos, mouse::Button::Left, None)
} }
} else { } else {
mouse::Click::new(pos, None) mouse::Click::new(pos, mouse::Button::Left, None)
}; };
self.prev_click = Some((new.clone(), now)); self.prev_click = Some((new.clone(), now));
new new
@ -300,7 +306,7 @@ where
tree: &mut Tree, tree: &mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
operation: &mut dyn Operation<OperationOutputWrapper<Message>>, operation: &mut dyn Operation,
) { ) {
self.content self.content
.as_widget() .as_widget()
@ -406,10 +412,11 @@ where
tree: &'b mut Tree, tree: &'b mut Tree,
layout: Layout<'_>, layout: Layout<'_>,
renderer: &Renderer, renderer: &Renderer,
translation: Vector,
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> { ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
self.content self.content
.as_widget_mut() .as_widget_mut()
.overlay(&mut tree.children[0], layout, renderer) .overlay(&mut tree.children[0], layout, renderer, translation)
} }
fn drag_destinations( fn drag_destinations(

View file

@ -11,10 +11,10 @@ use cosmic::{
futures, futures,
futures::SinkExt, futures::SinkExt,
keyboard::Modifiers, keyboard::Modifiers,
subscription::{self, Subscription}, stream,
//TODO: export in cosmic::widget //TODO: export in cosmic::widget
widget::{ widget::{
container, horizontal_rule, container, horizontal_rule, rule,
scrollable::{self, AbsoluteOffset, Viewport}, scrollable::{self, AbsoluteOffset, Viewport},
}, },
Alignment, Alignment,
@ -25,16 +25,16 @@ use cosmic::{
Point, Point,
Rectangle, Rectangle,
Size, Size,
Subscription,
}, },
iced_core::{mouse::ScrollDelta, widget::tree}, iced_core::{mouse::ScrollDelta, widget::tree},
iced_style::rule,
theme, theme,
widget::{ widget::{
self, self,
menu::{action::MenuAction, key_bind::KeyBind}, menu::{action::MenuAction, key_bind::KeyBind},
vertical_space, DndDestination, DndSource, Id, Widget, DndDestination, DndSource, Id, Space, Widget,
}, },
Element, Theme, Element,
}; };
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -122,9 +122,9 @@ fn button_appearance(
accent: bool, accent: bool,
condensed_radius: bool, condensed_radius: bool,
desktop: bool, desktop: bool,
) -> widget::button::Appearance { ) -> widget::button::Style {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
let mut appearance = widget::button::Appearance::new(); let mut appearance = widget::button::Style::new();
if selected { if selected {
if accent { if accent {
appearance.background = Some(Color::from(cosmic.accent_color()).into()); appearance.background = Some(Color::from(cosmic.accent_color()).into());
@ -938,6 +938,20 @@ impl Location {
} }
} }
pub struct TaskWrapper(pub cosmic::Task<Message>);
impl From<cosmic::Task<Message>> for TaskWrapper {
fn from(task: cosmic::Task<Message>) -> Self {
Self(task)
}
}
impl fmt::Debug for TaskWrapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TaskWrapper").finish()
}
}
#[derive(Debug)] #[derive(Debug)]
pub enum Command { pub enum Command {
Action(Action), Action(Action),
@ -946,7 +960,7 @@ pub enum Command {
ChangeLocation(String, Location, Option<PathBuf>), ChangeLocation(String, Location, Option<PathBuf>),
DropFiles(PathBuf, ClipboardPaste), DropFiles(PathBuf, ClipboardPaste),
EmptyTrash, EmptyTrash,
Iced(cosmic::Command<Message>), Iced(TaskWrapper),
MoveToTrash(Vec<PathBuf>), MoveToTrash(Vec<PathBuf>),
OpenFile(PathBuf), OpenFile(PathBuf),
OpenInNewTab(PathBuf), OpenInNewTab(PathBuf),
@ -1127,7 +1141,7 @@ impl ItemThumbnail {
let thumbnail = let thumbnail =
image.thumbnail(thumbnail_size, thumbnail_size).into_rgba8(); image.thumbnail(thumbnail_size, thumbnail_size).into_rgba8();
return ItemThumbnail::Image( return ItemThumbnail::Image(
widget::image::Handle::from_pixels( widget::image::Handle::from_rgba(
thumbnail.width(), thumbnail.width(),
thumbnail.height(), thumbnail.height(),
thumbnail.into_raw(), thumbnail.into_raw(),
@ -1186,7 +1200,7 @@ impl ItemThumbnail {
Ok(reader) => match reader.decode().map(|image| image.into_rgba8()) { Ok(reader) => match reader.decode().map(|image| image.into_rgba8()) {
Ok(image) => { Ok(image) => {
return ItemThumbnail::Image( return ItemThumbnail::Image(
widget::image::Handle::from_pixels( widget::image::Handle::from_rgba(
image.width(), image.width(),
image.height(), image.height(),
image.into_raw(), image.into_raw(),
@ -1315,9 +1329,9 @@ impl Item {
} }
column = column.push(widget::row::with_children(vec![ column = column.push(widget::row::with_children(vec![
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
self.preview(sizes), self.preview(sizes),
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
])); ]));
let mut details = widget::column().spacing(space_xxxs); let mut details = widget::column().spacing(space_xxxs);
@ -2223,23 +2237,25 @@ impl Tab {
self.select_rect(rect, mod_ctrl, mod_shift); self.select_rect(rect, mod_ctrl, mod_shift);
if self.select_focus.take().is_some() { if self.select_focus.take().is_some() {
// Unfocus currently focused button // Unfocus currently focused button
commands.push(Command::Iced(widget::button::focus(widget::Id::unique()))); commands.push(Command::Iced(
widget::button::focus(widget::Id::unique()).into(),
));
} }
} }
None => {} None => {}
}, },
Message::EditLocation(edit_location) => { Message::EditLocation(edit_location) => {
if self.edit_location.is_none() && edit_location.is_some() { if self.edit_location.is_none() && edit_location.is_some() {
commands.push(Command::Iced(widget::text_input::focus( commands.push(Command::Iced(
self.edit_location_id.clone(), widget::text_input::focus(self.edit_location_id.clone()).into(),
))); ));
} }
self.edit_location = edit_location; self.edit_location = edit_location;
} }
Message::EditLocationEnable => { Message::EditLocationEnable => {
commands.push(Command::Iced(widget::text_input::focus( commands.push(Command::Iced(
self.edit_location_id.clone(), widget::text_input::focus(self.edit_location_id.clone()).into(),
))); ));
self.edit_location = Some(self.location.clone()); self.edit_location = Some(self.location.clone());
} }
Message::OpenInNewTab(path) => { Message::OpenInNewTab(path) => {
@ -2281,13 +2297,12 @@ impl Tab {
self.select_position(row, col, mod_shift); self.select_position(row, col, mod_shift);
} }
if let Some(offset) = self.select_focus_scroll() { if let Some(offset) = self.select_focus_scroll() {
commands.push(Command::Iced(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
if let Some(id) = self.select_focus_id() { if let Some(id) = self.select_focus_id() {
commands.push(Command::Iced(widget::button::focus(id))); commands.push(Command::Iced(widget::button::focus(id).into()));
} }
} }
Message::GalleryToggle => { Message::GalleryToggle => {
@ -2342,13 +2357,12 @@ impl Tab {
self.select_position(0, 0, mod_shift); 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(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
if let Some(id) = self.select_focus_id() { if let Some(id) = self.select_focus_id() {
commands.push(Command::Iced(widget::button::focus(id))); commands.push(Command::Iced(widget::button::focus(id).into()));
} }
} }
} }
@ -2396,13 +2410,12 @@ impl Tab {
self.select_position(0, 0, mod_shift); 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(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
if let Some(id) = self.select_focus_id() { if let Some(id) = self.select_focus_id() {
commands.push(Command::Iced(widget::button::focus(id))); commands.push(Command::Iced(widget::button::focus(id).into()));
} }
} }
} }
@ -2433,13 +2446,12 @@ impl Tab {
self.select_position(0, 0, mod_shift); 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(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
if let Some(id) = self.select_focus_id() { if let Some(id) = self.select_focus_id() {
commands.push(Command::Iced(widget::button::focus(id))); commands.push(Command::Iced(widget::button::focus(id).into()));
} }
} }
} }
@ -2472,13 +2484,12 @@ impl Tab {
self.select_position(0, 0, mod_shift); 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(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
if let Some(id) = self.select_focus_id() { if let Some(id) = self.select_focus_id() {
commands.push(Command::Iced(widget::button::focus(id))); commands.push(Command::Iced(widget::button::focus(id).into()));
} }
} }
} }
@ -2587,10 +2598,9 @@ impl Tab {
} }
Message::ScrollToFocus => { Message::ScrollToFocus => {
if let Some(offset) = self.select_focus_scroll() { if let Some(offset) = self.select_focus_scroll() {
commands.push(Command::Iced(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
} }
Message::SearchContext(location, context) => { Message::SearchContext(location, context) => {
@ -2653,7 +2663,9 @@ impl Tab {
self.select_all(); self.select_all();
if self.select_focus.take().is_some() { if self.select_focus.take().is_some() {
// Unfocus currently focused button // Unfocus currently focused button
commands.push(Command::Iced(widget::button::focus(widget::Id::unique()))); commands.push(Command::Iced(
widget::button::focus(widget::Id::unique()).into(),
));
} }
} }
Message::SetSort(heading_option, dir) => { Message::SetSort(heading_option, dir) => {
@ -2756,13 +2768,16 @@ impl Tab {
Message::DndEnter(loc) => { Message::DndEnter(loc) => {
self.dnd_hovered = Some((loc.clone(), Instant::now())); self.dnd_hovered = Some((loc.clone(), Instant::now()));
if loc != self.location { if loc != self.location {
commands.push(Command::Iced(cosmic::Command::perform( commands.push(Command::Iced(
async move { cosmic::Task::perform(
tokio::time::sleep(HOVER_DURATION).await; async move {
Message::DndHover(loc) tokio::time::sleep(HOVER_DURATION).await;
}, Message::DndHover(loc)
|x| x, },
))); |x| x,
)
.into(),
));
} }
} }
Message::DndLeave(loc) => { Message::DndLeave(loc) => {
@ -2788,10 +2803,9 @@ impl Tab {
if self.scroll_opt.is_none() { if self.scroll_opt.is_none() {
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(scrollable::scroll_to( commands.push(Command::Iced(
self.scrollable_id.clone(), scrollable::scroll_to(self.scrollable_id.clone(), offset).into(),
offset, ));
)));
} }
// Change directory if requested // Change directory if requested
@ -2940,7 +2954,7 @@ impl Tab {
let location2 = location.clone(); let location2 = location.clone();
let location3 = location.clone(); let location3 = location.clone();
let is_dnd_hovered = self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&location); let is_dnd_hovered = self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&location);
widget::container( let mut container = widget::container(
DndDestination::for_data::<ClipboardPaste>(element, move |data, action| { DndDestination::for_data::<ClipboardPaste>(element, move |data, action| {
if let Some(mut data) = data { if let Some(mut data) = data {
if action == DndAction::Copy { if action == DndAction::Copy {
@ -2958,13 +2972,10 @@ impl Tab {
}) })
.on_enter(move |_, _, _| Message::DndEnter(location2.clone())) .on_enter(move |_, _, _| Message::DndEnter(location2.clone()))
.on_leave(move || Message::DndLeave(location3.clone())), .on_leave(move || Message::DndLeave(location3.clone())),
) );
.style(if is_dnd_hovered { if is_dnd_hovered {
theme::Container::custom(|t| { container = container.style(|t| {
let mut a = cosmic::iced_style::container::StyleSheet::appearance( let mut a = widget::container::Style::default();
t,
&theme::Container::default(),
);
let t = t.cosmic(); let t = t.cosmic();
// todo use theme drop target color // todo use theme drop target color
let mut bg = t.accent_color(); let mut bg = t.accent_color();
@ -2976,11 +2987,9 @@ impl Tab {
radius: t.radius_s().into(), radius: t.radius_s().into(),
}; };
a a
}) });
} else { }
theme::Container::default() container.into()
})
.into()
} }
pub fn gallery_view(&self) -> Element<Message> { pub fn gallery_view(&self) -> Element<Message> {
@ -3007,19 +3016,27 @@ impl Tab {
ItemThumbnail::Image(handle, _) => { ItemThumbnail::Image(handle, _) => {
if let Some(path) = item.path_opt() { if let Some(path) = item.path_opt() {
element_opt = Some( element_opt = Some(
//TODO: use widget::image::viewer, when its zoom can be reset widget::container(
widget::image(widget::image::Handle::from_path(path)) //TODO: use widget::image::viewer, when its zoom can be reset
.width(Length::Fill) widget::image(widget::image::Handle::from_path(path)),
.height(Length::Fill) )
.into(), .align_x(Alignment::Center)
.align_y(Alignment::Center)
.width(Length::Fill)
.height(Length::Fill)
.into(),
); );
} else { } else {
element_opt = Some( element_opt = Some(
//TODO: use widget::image::viewer, when its zoom can be reset widget::container(
widget::image(handle.clone()) //TODO: use widget::image::viewer, when its zoom can be reset
.width(Length::Fill) widget::image(handle.clone()),
.height(Length::Fill) )
.into(), .align_x(Alignment::Center)
.align_y(Alignment::Center)
.width(Length::Fill)
.height(Length::Fill)
.into(),
); );
} }
} }
@ -3040,20 +3057,20 @@ impl Tab {
} }
let mut column = widget::column::with_capacity(2); let mut column = widget::column::with_capacity(2);
column = column.push(widget::vertical_space(Length::Fixed(space_m.into()))); column = column.push(widget::Space::with_height(Length::Fixed(space_m.into())));
{ {
let mut row = widget::row::with_capacity(5).align_items(Alignment::Center); let mut row = widget::row::with_capacity(5).align_y(Alignment::Center);
row = row.push(widget::horizontal_space(Length::Fill)); row = row.push(widget::horizontal_space());
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(Length::Fill)); row = row.push(widget::horizontal_space());
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"))
.style(theme::Button::Standard) .class(theme::Button::Standard)
.on_press(Message::Gallery(false)), .on_press(Message::Gallery(false)),
); );
row = row.push(widget::horizontal_space(Length::Fixed(space_m.into()))); row = row.push(widget::Space::with_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_drag(|_| Message::WindowDrag) .on_drag(|_| Message::WindowDrag)
@ -3061,44 +3078,44 @@ impl Tab {
column = column.push(mouse_area); column = column.push(mouse_area);
} }
{ {
let mut row = widget::row::with_capacity(7).align_items(Alignment::Center); let mut row = widget::row::with_capacity(7).align_y(Alignment::Center);
row = row.push(widget::horizontal_space(Length::Fixed(space_m.into()))); row = row.push(widget::Space::with_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)
.style(theme::Button::Standard) .class(theme::Button::Standard)
.on_press(Message::GalleryPrevious), .on_press(Message::GalleryPrevious),
); );
row = row.push(widget::horizontal_space(Length::Fixed(space_xxs.into()))); row = row.push(widget::Space::with_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(widget::Space::new(Length::Fill, Length::Fill));
} }
row = row.push(widget::horizontal_space(Length::Fixed(space_xxs.into()))); row = row.push(widget::Space::with_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)
.style(theme::Button::Standard) .class(theme::Button::Standard)
.on_press(Message::GalleryNext), .on_press(Message::GalleryNext),
); );
row = row.push(widget::horizontal_space(Length::Fixed(space_m.into()))); row = row.push(widget::Space::with_width(Length::Fixed(space_m.into())));
column = column.push(row); column = column.push(row);
} }
widget::container(column) widget::container(column)
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill) .height(Length::Fill)
.style(theme::Container::Custom(Box::new(|theme| { .style(|theme| {
let cosmic = theme.cosmic(); let cosmic = theme.cosmic();
let mut bg = cosmic.bg_color(); let mut bg = cosmic.bg_color();
bg.alpha = 0.75; bg.alpha = 0.75;
widget::container::Appearance { widget::container::Style {
background: Some(Color::from(bg).into()), background: Some(Color::from(bg).into()),
..Default::default() ..Default::default()
} }
}))) })
.into() .into()
} }
@ -3110,7 +3127,7 @@ impl Tab {
font_size: f32, font_size: f32,
line_height: f32, line_height: f32,
) -> f32 { ) -> f32 {
let text: text::Text<'a, font::Font> = text::Text { let text: text::Text<&'a str, font::Font> = text::Text {
content, content,
bounds: Size::INFINITY, bounds: Size::INFINITY,
size: font_size.into(), size: font_size.into(),
@ -3119,7 +3136,7 @@ impl Tab {
horizontal_alignment: Horizontal::Left, horizontal_alignment: Horizontal::Left,
vertical_alignment: Vertical::Top, vertical_alignment: Vertical::Top,
shaping: text::Shaping::default(), shaping: text::Shaping::default(),
wrap: text::Wrap::None, wrapping: text::Wrapping::None,
}; };
graphics::text::Paragraph::with_text(text) graphics::text::Paragraph::with_text(text)
.min_bounds() .min_bounds()
@ -3144,14 +3161,14 @@ impl Tab {
let size = self.size_opt.get().unwrap_or(Size::new(0.0, 0.0)); let size = self.size_opt.get().unwrap_or(Size::new(0.0, 0.0));
let mut row = widget::row::with_capacity(5) let mut row = widget::row::with_capacity(5)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.padding([space_xxxs, 0]); .padding([space_xxxs, 0]);
let mut w = 0.0; let mut w = 0.0;
let mut prev_button = let mut prev_button =
widget::button::custom(widget::icon::from_name("go-previous-symbolic").size(16)) widget::button::custom(widget::icon::from_name("go-previous-symbolic").size(16))
.padding(space_xxs) .padding(space_xxs)
.style(theme::Button::Icon); .class(theme::Button::Icon);
if self.history_i > 0 && !self.history.is_empty() { if self.history_i > 0 && !self.history.is_empty() {
prev_button = prev_button.on_press(Message::GoPrevious); prev_button = prev_button.on_press(Message::GoPrevious);
} }
@ -3161,14 +3178,14 @@ impl Tab {
let mut next_button = let mut next_button =
widget::button::custom(widget::icon::from_name("go-next-symbolic").size(16)) widget::button::custom(widget::icon::from_name("go-next-symbolic").size(16))
.padding(space_xxs) .padding(space_xxs)
.style(theme::Button::Icon); .class(theme::Button::Icon);
if self.history_i + 1 < self.history.len() { if self.history_i + 1 < self.history.len() {
next_button = next_button.on_press(Message::GoNext); next_button = next_button.on_press(Message::GoNext);
} }
row = row.push(next_button); row = row.push(next_button);
w += 16.0 + 2.0 * space_xxs as f32; w += 16.0 + 2.0 * space_xxs as f32;
row = row.push(widget::horizontal_space(Length::Fixed(space_s.into()))); row = row.push(widget::Space::with_width(Length::Fixed(space_s.into())));
w += space_s as f32; w += space_s as f32;
//TODO: allow resizing? //TODO: allow resizing?
@ -3180,7 +3197,7 @@ impl Tab {
let (sort_name, sort_direction, _) = self.sort_options(); let (sort_name, sort_direction, _) = self.sort_options();
let heading_item = |name, width, msg| { let heading_item = |name, width, msg| {
let mut row = widget::row::with_capacity(2) let mut row = widget::row::with_capacity(2)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.width(width); .width(width);
row = row.push(widget::text::heading(name)); row = row.push(widget::text::heading(name));
@ -3216,7 +3233,7 @@ impl Tab {
}, },
heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size), heading_item(fl!("size"), Length::Fixed(size_width), HeadingOptions::Size),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.height(Length::Fixed((space_m + 4).into())) .height(Length::Fixed((space_m + 4).into()))
.padding([0, space_xxs]) .padding([0, space_xxs])
.spacing(space_xxs); .spacing(space_xxs);
@ -3230,7 +3247,7 @@ impl Tab {
) )
.on_press(Message::EditLocation(None)) .on_press(Message::EditLocation(None))
.padding(space_xxs) .padding(space_xxs)
.style(theme::Button::Icon), .class(theme::Button::Icon),
); );
row = row.push( row = row.push(
widget::text_input("", path.to_string_lossy()) widget::text_input("", path.to_string_lossy())
@ -3243,8 +3260,8 @@ impl Tab {
); );
let mut column = widget::column::with_capacity(4).padding([0, space_s]); let mut column = widget::column::with_capacity(4).padding([0, space_s]);
column = column.push(row); column = column.push(row);
column = column.push(horizontal_rule(1).style(theme::Rule::Custom(Box::new( column = column.push(horizontal_rule(1).class(theme::Rule::Custom(Box::new(
|theme: &Theme| rule::Appearance { |theme| rule::Style {
color: theme.cosmic().accent_color().into(), color: theme.cosmic().accent_color().into(),
width: 1, width: 1,
radius: 0.0.into(), radius: 0.0.into(),
@ -3262,7 +3279,7 @@ impl Tab {
crate::mouse_area::MouseArea::new( crate::mouse_area::MouseArea::new(
widget::button::custom(widget::icon::from_name("edit-symbolic").size(16)) widget::button::custom(widget::icon::from_name("edit-symbolic").size(16))
.padding(space_xxs) .padding(space_xxs)
.style(theme::Button::Icon) .class(theme::Button::Icon)
.on_press(Message::EditLocation(Some(self.location.clone()))), .on_press(Message::EditLocation(Some(self.location.clone()))),
) )
.on_middle_press(move |_| Message::OpenInNewTab(path.clone())), .on_middle_press(move |_| Message::OpenInNewTab(path.clone())),
@ -3280,7 +3297,7 @@ impl Tab {
let (name_width, name_text) = if children.is_empty() { let (name_width, name_text) = if children.is_empty() {
( (
text_width_heading(&name), text_width_heading(&name),
widget::text::heading(name).wrap(text::Wrap::None), widget::text::heading(name).wrapping(text::Wrapping::None),
) )
} else { } else {
children.push( children.push(
@ -3292,7 +3309,7 @@ impl Tab {
w += 16.0; w += 16.0;
( (
text_width_body(&name), text_width_body(&name),
widget::text::body(name).wrap(text::Wrap::None), widget::text::body(name).wrapping(text::Wrapping::None),
) )
}; };
@ -3300,7 +3317,7 @@ impl Tab {
w += 2.0 * space_xxxs as f32; w += 2.0 * space_xxxs as f32;
let mut row = widget::row::with_capacity(2) let mut row = widget::row::with_capacity(2)
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxxs); .spacing(space_xxxs);
//TODO: figure out why this hardcoded offset is needed after the first item is ellipsed //TODO: figure out why this hardcoded offset is needed after the first item is ellipsed
let overflow_offset = 64.0; let overflow_offset = 64.0;
@ -3317,7 +3334,7 @@ impl Tab {
let mut mouse_area = crate::mouse_area::MouseArea::new( let mut mouse_area = crate::mouse_area::MouseArea::new(
widget::button::custom(row) widget::button::custom(row)
.padding(space_xxxs) .padding(space_xxxs)
.style(theme::Button::Link) .class(theme::Button::Link)
.on_press(Message::Location(location.clone())), .on_press(Message::Location(location.clone())),
); );
@ -3351,7 +3368,7 @@ impl Tab {
widget::button::custom(widget::text::heading(fl!("trash"))) widget::button::custom(widget::text::heading(fl!("trash")))
.padding(space_xxxs) .padding(space_xxxs)
.on_press(Message::Location(Location::Trash)) .on_press(Message::Location(Location::Trash))
.style(theme::Button::Text) .class(theme::Button::Text)
.into(), .into(),
); );
} }
@ -3360,7 +3377,7 @@ impl Tab {
widget::button::custom(widget::text::heading(fl!("recents"))) widget::button::custom(widget::text::heading(fl!("recents")))
.padding(space_xxxs) .padding(space_xxxs)
.on_press(Message::Location(Location::Recents)) .on_press(Message::Location(Location::Recents))
.style(theme::Button::Text) .class(theme::Button::Text)
.into(), .into(),
); );
} }
@ -3372,7 +3389,7 @@ impl Tab {
uri.clone(), uri.clone(),
display_name.clone(), display_name.clone(),
))) )))
.style(theme::Button::Text) .class(theme::Button::Text)
.into(), .into(),
); );
} }
@ -3383,14 +3400,14 @@ impl Tab {
} }
let mut column = widget::column::with_capacity(4).padding([0, space_s]); let mut column = widget::column::with_capacity(4).padding([0, space_s]);
column = column.push(row); column = column.push(row);
column = column.push(horizontal_rule(1).style(theme::Rule::Custom(Box::new( column = column.push(
|theme: &Theme| rule::Appearance { horizontal_rule(1).class(theme::Rule::Custom(Box::new(|theme| rule::Style {
color: theme.cosmic().accent_color().into(), color: theme.cosmic().accent_color().into(),
width: 1, width: 1,
radius: 0.0.into(), radius: 0.0.into(),
fill_mode: rule::FillMode::Full, fill_mode: rule::FillMode::Full,
}, }))),
)))); );
if self.config.view == View::List && !condensed { if self.config.view == View::List && !condensed {
column = column.push(heading_row); column = column.push(heading_row);
@ -3435,7 +3452,7 @@ impl Tab {
], ],
Mode::Desktop => Vec::new(), Mode::Desktop => Vec::new(),
}) })
.align_items(Alignment::Center) .align_x(Alignment::Center)
.spacing(space_xxs), .spacing(space_xxs),
) )
.align_x(Horizontal::Center) .align_x(Horizontal::Center)
@ -3446,13 +3463,7 @@ impl Tab {
.into() .into()
} }
pub fn grid_view( pub fn grid_view(&self) -> (Option<Element<'static, Message>>, Element<Message>, bool) {
&self,
) -> (
Option<Element<'static, cosmic::app::Message<crate::app::Message>>>,
Element<Message>,
bool,
) {
let cosmic_theme::Spacing { let cosmic_theme::Spacing {
space_m, space_m,
space_xxs, space_xxs,
@ -3543,26 +3554,26 @@ impl Tab {
.size(icon_sizes.grid()), .size(icon_sizes.grid()),
) )
.padding(space_xxxs) .padding(space_xxxs)
.style(button_style(item.selected, false, false, false)) .class(button_style(item.selected, false, false, false))
.into(), .into(),
widget::tooltip( widget::tooltip(
widget::button::custom(widget::text::body(&item.display_name)) widget::button::custom(widget::text::body(&item.display_name))
.id(item.button_id.clone()) .id(item.button_id.clone())
.padding([0, space_xxxs]) .padding([0, space_xxxs])
.style(button_style( .class(button_style(
item.selected, item.selected,
true, true,
true, true,
matches!(self.mode, Mode::Desktop), matches!(self.mode, Mode::Desktop),
)), )),
&item.name, widget::text::body(&item.name),
widget::tooltip::Position::Bottom, widget::tooltip::Position::Bottom,
) )
.into(), .into(),
]; ];
let mut column = widget::column::with_capacity(buttons.len()) let mut column = widget::column::with_capacity(buttons.len())
.align_items(Alignment::Center) .align_x(Alignment::Center)
.height(Length::Fixed(item_height as f32)) .height(Length::Fixed(item_height as f32))
.width(Length::Fixed(item_width as f32)); .width(Length::Fixed(item_width as f32));
for button in buttons { for button in buttons {
@ -3660,7 +3671,7 @@ impl Tab {
let spacer_height = height.checked_sub(max_bottom + top_deduct).unwrap_or(0); let spacer_height = height.checked_sub(max_bottom + top_deduct).unwrap_or(0);
if spacer_height > 0 { if spacer_height > 0 {
children.push( children.push(
widget::container(vertical_space(Length::Fixed(spacer_height as f32))) widget::container(Space::with_height(Length::Fixed(spacer_height as f32)))
.into(), .into(),
) )
} }
@ -3690,7 +3701,7 @@ impl Tab {
) )
.on_press(Message::Click(Some(*i))) .on_press(Message::Click(Some(*i)))
.padding(space_xxxs) .padding(space_xxxs)
.style(button_style( .class(button_style(
item.selected, item.selected,
false, false,
false, false,
@ -3700,11 +3711,11 @@ impl Tab {
.id(item.button_id.clone()) .id(item.button_id.clone())
.on_press(Message::Click(Some(*i))) .on_press(Message::Click(Some(*i)))
.padding([0, space_xxxs]) .padding([0, space_xxxs])
.style(button_style(item.selected, true, true, false)), .class(button_style(item.selected, true, true, false)),
]; ];
let mut column = widget::column::with_capacity(buttons.len()) let mut column = widget::column::with_capacity(buttons.len())
.align_items(Alignment::Center) .align_x(Alignment::Center)
.height(Length::Fixed(item_height as f32)) .height(Length::Fixed(item_height as f32))
.width(Length::Fixed(item_width as f32)); .width(Length::Fixed(item_width as f32));
for button in buttons { for button in buttons {
@ -3715,14 +3726,13 @@ impl Tab {
dnd_item_i += 1; dnd_item_i += 1;
} else { } else {
dnd_grid = dnd_grid.push( dnd_grid = dnd_grid.push(
widget::container(vertical_space(item_width as f32)) widget::container(Space::with_height(item_width as f32))
.height(Length::Fixed(item_height as f32)), .height(Length::Fixed(item_height as f32)),
); );
} }
} }
} }
Element::from(dnd_grid) Element::from(dnd_grid)
.map(|m| cosmic::app::Message::App(crate::app::Message::TabMessage(None, m)))
}), }),
mouse_area::MouseArea::new( mouse_area::MouseArea::new(
widget::container(widget::column::with_children(children)).width(Length::Fill), widget::container(widget::column::with_children(children)).width(Length::Fill),
@ -3737,13 +3747,7 @@ impl Tab {
) )
} }
pub fn list_view( pub fn list_view(&self) -> (Option<Element<'static, Message>>, Element<Message>, bool) {
&self,
) -> (
Option<Element<'static, cosmic::app::Message<crate::app::Message>>>,
Element<Message>,
bool,
) {
let cosmic_theme::Spacing { let cosmic_theme::Spacing {
space_m, space_m,
space_s, space_s,
@ -3863,7 +3867,7 @@ impl Tab {
.into(), .into(),
]) ])
.height(Length::Fixed(row_height as f32)) .height(Length::Fixed(row_height as f32))
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
} else if is_search { } else if is_search {
widget::row::with_children(vec![ widget::row::with_children(vec![
@ -3889,7 +3893,7 @@ impl Tab {
.into(), .into(),
]) ])
.height(Length::Fixed(row_height as f32)) .height(Length::Fixed(row_height as f32))
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
} else { } else {
widget::row::with_children(vec![ widget::row::with_children(vec![
@ -3908,7 +3912,7 @@ impl Tab {
.into(), .into(),
]) ])
.height(Length::Fixed(row_height as f32)) .height(Length::Fixed(row_height as f32))
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
}; };
@ -3918,7 +3922,7 @@ impl Tab {
.width(Length::Fill) .width(Length::Fill)
.id(item.button_id.clone()) .id(item.button_id.clone())
.padding([0, space_xxs]) .padding([0, space_xxs])
.style(button_style(item.selected, true, false, false)), .class(button_style(item.selected, true, false, false)),
) )
.on_press(move |_| Message::Click(Some(i))) .on_press(move |_| Message::Click(Some(i)))
.on_double_click(move |_| Message::DoubleClick(Some(i))) .on_double_click(move |_| Message::DoubleClick(Some(i)))
@ -3944,7 +3948,7 @@ 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(vertical_space(Length::Fixed(row_height as f32))) Element::from(Space::with_height(Length::Fixed(row_height as f32)))
} else if condensed { } else if condensed {
widget::row::with_children(vec![ widget::row::with_children(vec![
widget::icon::icon(item.icon_handle_list_condensed.clone()) widget::icon::icon(item.icon_handle_list_condensed.clone())
@ -3958,7 +3962,7 @@ impl Tab {
]) ])
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
} else if is_search { } else if is_search {
@ -3984,7 +3988,7 @@ impl Tab {
.width(Length::Fixed(size_width)) .width(Length::Fixed(size_width))
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
} else { } else {
@ -4003,7 +4007,7 @@ impl Tab {
.width(Length::Fixed(size_width)) .width(Length::Fixed(size_width))
.into(), .into(),
]) ])
.align_items(Alignment::Center) .align_y(Alignment::Center)
.spacing(space_xxs) .spacing(space_xxs)
.into() .into()
}; };
@ -4039,14 +4043,13 @@ impl Tab {
let spacer_height = size.height - y as f32 - top_deduct as f32; let spacer_height = size.height - y as f32 - top_deduct as f32;
if spacer_height > 0. { if spacer_height > 0. {
children children.push(
.push(widget::container(vertical_space(Length::Fixed(spacer_height))).into()); widget::container(Space::with_height(Length::Fixed(spacer_height))).into(),
);
} }
} }
let drag_col = (!drag_items.is_empty()).then(|| { let drag_col = (!drag_items.is_empty())
Element::from(widget::column::with_children(drag_items)) .then(|| Element::from(widget::column::with_children(drag_items)));
.map(|m| cosmic::app::Message::App(crate::app::Message::TabMessage(None, m)))
});
( (
drag_col, drag_col,
@ -4099,22 +4102,19 @@ impl Tab {
.collect::<Vec<PathBuf>>() .collect::<Vec<PathBuf>>()
}) })
.unwrap_or_default(); .unwrap_or_default();
let item_view = DndSource::<_, cosmic::app::Message<app::Message>, ClipboardCopy>::with_id( let item_view =
item_view, DndSource::<Message, ClipboardCopy>::with_id(item_view, Id::new("tab-view"));
Id::new("tab-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 = ArcElementWrapper(Arc::new(Mutex::new(drag_list))); let drag_list = ArcElementWrapper::<Message>(Arc::new(Mutex::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)
}) })
.drag_icon(move || { .drag_icon(move || {
let state: tree::State = let state: tree::State = Widget::<Message, _, _>::state(&drag_list);
Widget::<cosmic::app::Message<app::Message>, _, _>::state(&drag_list); (Element::from(drag_list.clone()).map(|_m| ()), state)
(drag_list.clone().into(), state)
}) })
} }
_ => item_view, _ => item_view,
@ -4164,7 +4164,7 @@ impl Tab {
if !items.is_empty() { if !items.is_empty() {
tab_column = tab_column.push( tab_column = tab_column.push(
widget::layer_container(widget::row::with_children(vec![ widget::layer_container(widget::row::with_children(vec![
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().into(),
widget::button::standard(fl!("empty-trash")) widget::button::standard(fl!("empty-trash"))
.on_press(Message::EmptyTrash) .on_press(Message::EmptyTrash)
.into(), .into(),
@ -4178,7 +4178,7 @@ impl Tab {
Location::Network(uri, _display_name) if uri == "network:///" => { Location::Network(uri, _display_name) if uri == "network:///" => {
tab_column = tab_column.push( tab_column = tab_column.push(
widget::layer_container(widget::row::with_children(vec![ widget::layer_container(widget::row::with_children(vec![
widget::horizontal_space(Length::Fill).into(), widget::horizontal_space().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(),
@ -4194,11 +4194,8 @@ impl Tab {
.width(Length::Fill); .width(Length::Fill);
if self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&tab_location) { if self.dnd_hovered.as_ref().map(|(l, _)| l) == Some(&tab_location) {
tab_view = tab_view.style(cosmic::theme::Container::custom(|t| { tab_view = tab_view.style(|t| {
let mut a = cosmic::iced_style::container::StyleSheet::appearance( let mut a = widget::container::Style::default();
t,
&cosmic::theme::Container::default(),
);
let c = t.cosmic(); let c = t.cosmic();
a.border = cosmic::iced_core::Border { a.border = cosmic::iced_core::Border {
color: (c.accent_color()).into(), color: (c.accent_color()).into(),
@ -4206,7 +4203,7 @@ impl Tab {
radius: c.radius_0().into(), radius: c.radius_0().into(),
}; };
a a
})); });
} }
let tab_location_2 = self.location.clone(); let tab_location_2 = self.location.clone();
@ -4287,10 +4284,9 @@ impl Tab {
continue; continue;
}; };
let mime = item.mime.clone(); let mime = item.mime.clone();
subscriptions.push(subscription::channel( subscriptions.push(Subscription::run_with_id(
path.clone(), path.clone(),
1, stream::channel(1, |mut output| async move {
|mut output| async move {
let message = { let message = {
let path = path.clone(); let path = path.clone();
tokio::task::spawn_blocking(move || { tokio::task::spawn_blocking(move || {
@ -4312,7 +4308,7 @@ impl Tab {
} }
std::future::pending().await std::future::pending().await
}, }),
)); ));
if subscriptions.len() >= jobs { if subscriptions.len() >= jobs {
@ -4327,10 +4323,9 @@ impl Tab {
let term = term.clone(); let term = term.clone();
let show_hidden = *show_hidden; let show_hidden = *show_hidden;
let start = start.clone(); let start = start.clone();
subscriptions.push(subscription::channel( subscriptions.push(Subscription::run_with_id(
location.clone(), location.clone(),
2, stream::channel(2, move |mut output| async move {
move |mut output| async move {
//TODO: optimal size? //TODO: optimal size?
let (results_tx, results_rx) = mpsc::channel(65536); let (results_tx, results_rx) = mpsc::channel(65536);
@ -4408,7 +4403,7 @@ 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
}, }),
)); ));
} }
@ -4846,7 +4841,7 @@ impl<M> Widget<M, cosmic::Theme, cosmic::Renderer> for ArcElementWrapper<M> {
state: &mut tree::Tree, state: &mut tree::Tree,
layout: cosmic::iced_core::Layout<'_>, layout: cosmic::iced_core::Layout<'_>,
renderer: &cosmic::Renderer, renderer: &cosmic::Renderer,
operation: &mut dyn widget::Operation<cosmic::iced_core::widget::OperationOutputWrapper<M>>, operation: &mut dyn widget::Operation,
) { ) {
self.0 self.0
.lock() .lock()
@ -4891,6 +4886,7 @@ impl<M> Widget<M, cosmic::Theme, cosmic::Renderer> for ArcElementWrapper<M> {
_state: &'a mut tree::Tree, _state: &'a mut tree::Tree,
_layout: cosmic::iced_core::Layout<'_>, _layout: cosmic::iced_core::Layout<'_>,
_renderer: &cosmic::Renderer, _renderer: &cosmic::Renderer,
_translation: cosmic::iced_core::Vector,
) -> Option<cosmic::iced_core::overlay::Element<'a, M, cosmic::Theme, cosmic::Renderer>> { ) -> Option<cosmic::iced_core::overlay::Element<'a, M, cosmic::Theme, cosmic::Renderer>> {
// TODO // TODO
None None