Improve dialog handling

This commit is contained in:
Jeremy Soller 2024-02-13 12:29:50 -07:00
parent dea974469a
commit db485798da
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
7 changed files with 273 additions and 93 deletions

79
Cargo.lock generated
View file

@ -870,18 +870,18 @@ dependencies = [
[[package]]
name = "clipboard_wayland"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8134163bd07c47ae3cc29babc42c255fdb315facc790950ae2d0e561ea6f2ec0"
checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8"
dependencies = [
"smithay-clipboard",
]
[[package]]
name = "clipboard_x11"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cf45b436634fee64c6d3981639b46a87eeea3c64e422643273fcefd1baef56c"
checksum = "4274ea815e013e0f9f04a2633423e14194e408a0576c943ce3d14ca56c50031c"
dependencies = [
"thiserror",
"x11rb",
@ -1122,7 +1122,7 @@ dependencies = [
[[package]]
name = "cosmic-config"
version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"atomicwrites",
"cosmic-config-derive",
@ -1139,7 +1139,7 @@ dependencies = [
[[package]]
name = "cosmic-config-derive"
version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"quote",
"syn 1.0.109",
@ -1175,7 +1175,7 @@ dependencies = [
[[package]]
name = "cosmic-text"
version = "0.11.2"
source = "git+https://github.com/pop-os/cosmic-text.git#0cb6eba6e708e2743313ee0016162de7a0146353"
source = "git+https://github.com/pop-os/cosmic-text.git#85821731285bf88d712dce8e7410453d908bc038"
dependencies = [
"bitflags 2.4.2",
"fontdb",
@ -1197,7 +1197,7 @@ dependencies = [
[[package]]
name = "cosmic-theme"
version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"almost",
"cosmic-config",
@ -2017,9 +2017,9 @@ dependencies = [
[[package]]
name = "freedesktop-icons"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5339cbd60b2ff6b95ef212ab96bc80bf1a9dff2821b9966c417cdfae2808796"
checksum = "a8ef34245e0540c9a3ce7a28340b98d2c12b75da0d446da4e8224923fcaa0c16"
dependencies = [
"dirs",
"once_cell",
@ -2460,9 +2460,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.5"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
[[package]]
name = "hex"
@ -2578,7 +2578,7 @@ dependencies = [
[[package]]
name = "iced"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_accessibility",
"iced_core",
@ -2593,7 +2593,7 @@ dependencies = [
[[package]]
name = "iced_accessibility"
version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"accesskit",
"accesskit_winit",
@ -2602,7 +2602,7 @@ dependencies = [
[[package]]
name = "iced_core"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"bitflags 1.3.2",
"log",
@ -2619,7 +2619,7 @@ dependencies = [
[[package]]
name = "iced_futures"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"futures",
"iced_core",
@ -2632,7 +2632,7 @@ dependencies = [
[[package]]
name = "iced_graphics"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"bitflags 1.3.2",
"bytemuck",
@ -2656,7 +2656,7 @@ dependencies = [
[[package]]
name = "iced_renderer"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_graphics",
"iced_tiny_skia",
@ -2668,7 +2668,7 @@ dependencies = [
[[package]]
name = "iced_runtime"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_core",
"iced_futures",
@ -2678,7 +2678,7 @@ dependencies = [
[[package]]
name = "iced_style"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_core",
"once_cell",
@ -2688,7 +2688,7 @@ dependencies = [
[[package]]
name = "iced_tiny_skia"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"bytemuck",
"cosmic-text",
@ -2705,7 +2705,7 @@ dependencies = [
[[package]]
name = "iced_wgpu"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"bitflags 1.3.2",
"bytemuck",
@ -2724,7 +2724,7 @@ dependencies = [
[[package]]
name = "iced_widget"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_renderer",
"iced_runtime",
@ -2738,7 +2738,7 @@ dependencies = [
[[package]]
name = "iced_winit"
version = "0.12.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"iced_graphics",
"iced_runtime",
@ -3066,7 +3066,7 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libcosmic"
version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic.git#02cee1d8051f504860d9b961245919fde1542355"
source = "git+https://github.com/pop-os/libcosmic.git#b430656966a24158270912a2ff827c77aae39de4"
dependencies = [
"apply",
"ashpd",
@ -5231,7 +5231,7 @@ dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit 0.22.4",
"toml_edit 0.22.5",
]
[[package]]
@ -5251,7 +5251,7 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
"winnow 0.5.40",
]
[[package]]
@ -5262,20 +5262,20 @@ checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
"winnow 0.5.40",
]
[[package]]
name = "toml_edit"
version = "0.22.4"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ffdf896f8daaabf9b66ba8e77ea1ed5ed0f72821b398aba62352e95062951"
checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
"winnow 0.6.0",
]
[[package]]
@ -6009,9 +6009,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window_clipboard"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6a197337269a469b5b2583d65dd7dfe475fd79a525be0aa647ff6d37ac6612c"
checksum = "f6d692d46038c433f9daee7ad8757e002a4248c20b0a3fbc991d99521d3bcb6d"
dependencies = [
"clipboard-win",
"clipboard_macos",
@ -6361,9 +6361,18 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.5.39"
version = "0.5.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29"
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
dependencies = [
"memchr",
]
[[package]]
name = "winnow"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b1dbce9e90e5404c5a52ed82b1d13fc8cfbdad85033b6f57546ffd1265f8451"
dependencies = [
"memchr",
]

View file

@ -1,11 +1,113 @@
use cosmic::{
app::{self, Command, Core, Settings},
executor,
iced::{subscription::Subscription, window},
widget, Application, Element,
};
use cosmic_files::dialog::{Dialog, DialogMessage, DialogResult};
fn main() -> Result<(), Box<dyn std::error::Error>> {
match cosmic_files::dialog()? {
Some(paths) => {
for path in paths {
println!("{}", path.display());
}
}
None => {}
}
let settings = Settings::default();
app::run::<App>(settings, ())?;
Ok(())
}
#[derive(Clone, Debug)]
pub enum Message {
DialogMessage(DialogMessage),
DialogOpen,
DialogResult(DialogResult),
}
pub struct App {
core: Core,
dialog_opt: Option<Dialog<Message>>,
result_opt: Option<DialogResult>,
}
impl Application for App {
type Executor = executor::Default;
type Flags = ();
type Message = Message;
const APP_ID: &'static str = "com.system76.CosmicFilesDialogExample";
fn core(&self) -> &Core {
&self.core
}
fn core_mut(&mut self) -> &mut Core {
&mut self.core
}
fn init(core: Core, _flags: Self::Flags) -> (Self, Command<Message>) {
(
Self {
core,
dialog_opt: None,
result_opt: None,
},
Command::none(),
)
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::DialogMessage(dialog_message) => {
if let Some(dialog) = &mut self.dialog_opt {
return dialog.update(dialog_message);
}
}
Message::DialogOpen => {
if self.dialog_opt.is_none() {
let (dialog, command) =
Dialog::new(Message::DialogMessage, Message::DialogResult);
self.dialog_opt = Some(dialog);
return command;
}
}
Message::DialogResult(result) => {
self.dialog_opt = None;
self.result_opt = Some(result);
}
}
Command::none()
}
fn view_window(&self, window_id: window::Id) -> Element<Message> {
match &self.dialog_opt {
Some(dialog) => dialog.view(window_id),
None => widget::text("No dialog").into(),
}
}
fn view(&self) -> Element<Message> {
let mut button = widget::button(widget::text("Open Dialog"));
if self.dialog_opt.is_none() {
button = button.on_press(Message::DialogOpen);
}
let mut column = widget::column();
column = column.push(button);
if let Some(result) = &self.result_opt {
match result {
DialogResult::Cancel => {
column = column.push(widget::text("Cancel"));
}
DialogResult::Open(paths) => {
for path in paths.iter() {
column = column.push(widget::text(format!("{}", path.display())));
}
}
}
}
column.into()
}
fn subscription(&self) -> Subscription<Message> {
match &self.dialog_opt {
Some(dialog) => dialog.subscription(),
None => Subscription::none(),
}
}
}

View file

@ -2,12 +2,17 @@
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::{
app::{message, Command, Core},
app::{
self,
cosmic::{Cosmic, Message as CosmicMessage},
message, Command, Core,
},
cosmic_theme, executor,
iced::{
event,
futures::{self, SinkExt},
keyboard::{Event as KeyEvent, Modifiers},
multi_window::Application as IcedApplication,
subscription::{self, Subscription},
window, Event, Length,
},
@ -31,13 +36,89 @@ use crate::{
};
#[derive(Clone, Debug)]
pub struct Flags {
pub result_lock: Arc<Mutex<Option<Vec<PathBuf>>>>,
pub struct DialogMessage(app::Message<Message>);
#[derive(Clone, Debug)]
pub enum DialogResult {
Cancel,
Open(Vec<PathBuf>),
}
pub struct Dialog<M> {
cosmic: Cosmic<App>,
mapper: fn(DialogMessage) -> M,
on_result: fn(DialogResult) -> M,
}
impl<M: 'static> Dialog<M> {
pub fn new(
mapper: fn(DialogMessage) -> M,
on_result: fn(DialogResult) -> M,
) -> (Self, Command<M>) {
let mut settings = window::Settings::default();
settings.decorations = false;
settings.exit_on_close_request = false;
settings.transparent = true;
settings.platform_specific.application_id = App::APP_ID.to_string();
let (window_id, window_command) = window::spawn(settings);
let core = Core::default();
let flags = Flags { window_id };
let (cosmic, cosmic_command) = <Cosmic<App> as IcedApplication>::new((core, flags));
(
Self {
cosmic,
mapper,
on_result,
},
Command::batch([window_command, cosmic_command])
.map(DialogMessage)
.map(move |message| app::Message::App(mapper(message))),
)
}
pub fn subscription(&self) -> Subscription<M> {
self.cosmic
.subscription()
.map(DialogMessage)
.map(self.mapper)
}
pub fn update(&mut self, message: DialogMessage) -> Command<M> {
let mapper = self.mapper;
let command = self
.cosmic
.update(message.0)
.map(DialogMessage)
.map(move |message| app::Message::App(mapper(message)));
if let Some(result) = self.cosmic.app.result_opt.take() {
let on_result = self.on_result;
Command::batch([
command,
Command::perform(async move { app::Message::App(on_result(result)) }, |x| x),
])
} else {
command
}
}
pub fn view(&self, window_id: window::Id) -> Element<M> {
self.cosmic
.view(window_id)
.map(DialogMessage)
.map(self.mapper)
}
}
#[derive(Clone, Debug)]
struct Flags {
window_id: window::Id,
}
/// Messages that are used specifically by our [`App`].
#[derive(Clone, Debug)]
pub enum Message {
enum Message {
Cancel,
Modifiers(Modifiers),
NotifyEvent(notify::Event),
@ -51,7 +132,7 @@ pub enum Message {
}
#[derive(Debug)]
pub struct WatcherWrapper {
struct WatcherWrapper {
watcher_opt: Option<notify::RecommendedWatcher>,
}
@ -68,12 +149,13 @@ impl PartialEq for WatcherWrapper {
}
/// The [`App`] stores application-specific state.
pub struct App {
struct App {
core: Core,
flags: Flags,
nav_model: segmented_button::SingleSelectModel,
tab_model: segmented_button::Model<segmented_button::SingleSelect>,
modifiers: Modifiers,
nav_model: segmented_button::SingleSelectModel,
result_opt: Option<DialogResult>,
tab_model: segmented_button::Model<segmented_button::SingleSelect>,
watcher_opt: Option<(notify::RecommendedWatcher, HashSet<PathBuf>)>,
}
@ -124,7 +206,7 @@ impl App {
None => (String::new(), "COSMIC File Manager".to_string()),
};
self.set_header_title(header_title);
self.set_window_title(window_title, window::Id::MAIN)
self.set_window_title(window_title, self.main_window_id())
}
fn update_watcher(&mut self) -> Command<Message> {
@ -187,7 +269,7 @@ impl Application for App {
type Message = Message;
/// The unique application ID to supply to the window manager.
const APP_ID: &'static str = "com.system76.CosmicFiles";
const APP_ID: &'static str = "com.system76.CosmicFilesDialog";
fn core(&self) -> &Core {
&self.core
@ -198,7 +280,7 @@ impl Application for App {
}
/// 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, Command<Message>) {
core.window.show_maximize = false;
core.window.show_minimize = false;
@ -232,9 +314,10 @@ impl Application for App {
let mut app = App {
core,
flags,
nav_model: nav_model.build(),
tab_model: segmented_button::ModelBuilder::default().build(),
modifiers: Modifiers::empty(),
nav_model: nav_model.build(),
result_opt: None,
tab_model: segmented_button::ModelBuilder::default().build(),
watcher_opt: None,
};
@ -247,8 +330,12 @@ impl Application for App {
(app, Command::batch(commands))
}
fn main_window_id(&self) -> window::Id {
self.flags.window_id
}
// The default nav_bar widget needs to have its width reduced for cosmic-files
fn nav_bar(&self) -> Option<Element<message::Message<Self::Message>>> {
fn nav_bar(&self) -> Option<Element<message::Message<Message>>> {
if !self.core().nav_bar_active() {
return None;
}
@ -270,7 +357,11 @@ impl Application for App {
Some(&self.nav_model)
}
fn on_nav_select(&mut self, entity: segmented_button::Entity) -> Command<Self::Message> {
fn on_app_exit(&mut self) {
self.result_opt = Some(DialogResult::Cancel);
}
fn on_nav_select(&mut self, entity: segmented_button::Entity) -> Command<Message> {
let location_opt = self.nav_model.data::<Location>(entity).clone();
if let Some(location) = location_opt {
@ -282,11 +373,11 @@ impl Application for App {
}
/// Handle application events here.
fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Cancel => {
*self.flags.result_lock.lock().unwrap() = None;
return window::close(window::Id::MAIN);
self.result_opt = Some(DialogResult::Cancel);
return window::close(self.main_window_id());
}
Message::Modifiers(modifiers) => {
self.modifiers = modifiers;
@ -341,8 +432,8 @@ impl Application for App {
}
}
}
*self.flags.result_lock.lock().unwrap() = Some(paths);
return window::close(window::Id::MAIN);
self.result_opt = Some(DialogResult::Open(paths));
return window::close(self.main_window_id());
}
Message::SelectAll(entity_opt) => {
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
@ -379,7 +470,7 @@ impl Application for App {
// If that was the last tab, close window
if self.tab_model.iter().next().is_none() {
return window::close(window::Id::MAIN);
return window::close(self.main_window_id());
}
return Command::batch([self.update_title(), self.update_watcher()]);
@ -417,7 +508,7 @@ impl Application for App {
}
/// Creates a view after each update.
fn view(&self) -> Element<Self::Message> {
fn view(&self) -> Element<Message> {
let cosmic_theme::Spacing { space_xxs, .. } = self.core().system_theme().cosmic().spacing;
let mut tab_column = widget::column::with_capacity(1);
@ -470,7 +561,7 @@ impl Application for App {
content
}
fn subscription(&self) -> Subscription<Self::Message> {
fn subscription(&self) -> Subscription<Message> {
struct WatcherSubscription;
Subscription::batch([

View file

@ -6,11 +6,7 @@ use cosmic::{
cosmic_config::{self, CosmicConfigEntry},
iced::Limits,
};
use std::{
path::PathBuf,
process,
sync::{Arc, Mutex},
};
use std::{path::PathBuf, process};
use app::{App, Flags};
mod app;
@ -35,24 +31,6 @@ pub fn home_dir() -> PathBuf {
}
}
/// Runs application with these settings
pub fn dialog() -> Result<Option<Vec<PathBuf>>, Box<dyn std::error::Error>> {
localize::localize();
let mut settings = Settings::default();
let mut result_lock = Arc::new(Mutex::new(None));
let flags = dialog::Flags {
result_lock: result_lock.clone(),
};
cosmic::app::run::<dialog::App>(settings, flags)?;
let mut result_guard = result_lock.lock().unwrap();
let result = result_guard.take();
Ok(result)
}
/// Runs application with these settings
#[rustfmt::skip]
pub fn main() -> Result<(), Box<dyn std::error::Error>> {

View file

@ -14,10 +14,10 @@ use cosmic::{
use std::collections::HashMap;
use crate::{
app::{Action, ContextPage, Message},
app::{Action, Message},
fl,
key_bind::KeyBind,
tab::{self, Location, Tab},
tab::{Location, Tab},
};
macro_rules! menu_button {

View file

@ -1,5 +1,5 @@
use cosmic::iced::futures::{channel::mpsc, SinkExt};
use std::{error::Error, future::Future, io, path::PathBuf, time};
use std::path::PathBuf;
use crate::app::Message;

View file

@ -23,7 +23,7 @@ use std::{
time::{Duration, Instant},
};
use crate::{config::TabConfig, fl, home_dir, mime_icon::mime_icon};
use crate::{config::TabConfig, fl, mime_icon::mime_icon};
const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500);
//TODO: configurable