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

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