Improve dialog handling
This commit is contained in:
parent
dea974469a
commit
db485798da
7 changed files with 273 additions and 93 deletions
137
src/dialog.rs
137
src/dialog.rs
|
|
@ -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([
|
||||
|
|
|
|||
24
src/lib.rs
24
src/lib.rs
|
|
@ -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>> {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue