feat: add default applications page
This commit is contained in:
parent
4e310024b7
commit
508b05135a
24 changed files with 682 additions and 205 deletions
|
|
@ -42,6 +42,7 @@ itertools = "0.13.0"
|
|||
itoa = "1.0.11"
|
||||
libcosmic.workspace = true
|
||||
locale1 = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||
mime-apps = { package = "cosmic-mime-apps", git = "https://github.com/pop-os/cosmic-mime-apps", optional = true }
|
||||
notify = "6.1.1"
|
||||
once_cell = "1.19.0"
|
||||
regex = "1.10.6"
|
||||
|
|
@ -67,6 +68,7 @@ zbus = { version = "4.4.0", features = ["tokio"], optional = true }
|
|||
ustr = "1.0.0"
|
||||
fontdb = "0.16.2"
|
||||
fixed_decimal = "0.5.6"
|
||||
mime = "0.3.17"
|
||||
|
||||
[dependencies.cosmic-settings-subscriptions]
|
||||
git = "https://github.com/pop-os/cosmic-settings-subscriptions"
|
||||
|
|
@ -96,6 +98,7 @@ linux = [
|
|||
"page-about",
|
||||
"page-bluetooth",
|
||||
"page-date",
|
||||
"page-default-apps",
|
||||
"page-input",
|
||||
"page-networking",
|
||||
"page-power",
|
||||
|
|
@ -111,6 +114,7 @@ linux = [
|
|||
page-about = ["dep:cosmic-settings-system", "dep:hostname1-zbus", "dep:zbus"]
|
||||
page-bluetooth = ["dep:bluez-zbus", "dep:zbus"]
|
||||
page-date = ["dep:timedate-zbus", "dep:zbus"]
|
||||
page-default-apps = ["dep:mime-apps"]
|
||||
page-input = [
|
||||
"dep:cosmic-comp-config",
|
||||
"dep:cosmic-settings-config",
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ use desktop::{
|
|||
use event::wayland;
|
||||
use page::Entity;
|
||||
use std::collections::BTreeSet;
|
||||
use std::time::Duration;
|
||||
use std::{borrow::Cow, str::FromStr};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
|
|
@ -82,6 +81,8 @@ impl SettingsApp {
|
|||
PageCommands::Bluetooth => self.pages.page_id::<bluetooth::Page>(),
|
||||
#[cfg(feature = "page-date")]
|
||||
PageCommands::DateTime => self.pages.page_id::<time::date::Page>(),
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
PageCommands::DefaultApps => self.pages.page_id::<system::default_apps::Page>(),
|
||||
PageCommands::Desktop => self.pages.page_id::<desktop::Page>(),
|
||||
PageCommands::Displays => self.pages.page_id::<display::Page>(),
|
||||
#[cfg(feature = "wayland")]
|
||||
|
|
@ -212,10 +213,7 @@ impl cosmic::Application for SettingsApp {
|
|||
}
|
||||
.unwrap_or(desktop_id);
|
||||
|
||||
(
|
||||
app,
|
||||
cosmic::command::message(Message::DelayedInit(active_id)),
|
||||
)
|
||||
(app, cosmic::task::message(Message::DelayedInit(active_id)))
|
||||
}
|
||||
|
||||
fn nav_model(&self) -> Option<&nav_bar::Model> {
|
||||
|
|
@ -389,6 +387,13 @@ impl cosmic::Application for SettingsApp {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
crate::pages::Message::DefaultApps(message) => {
|
||||
if let Some(page) = self.pages.page_mut::<system::default_apps::Page>() {
|
||||
return page.update(message).map(Into::into);
|
||||
}
|
||||
}
|
||||
|
||||
crate::pages::Message::Desktop(message) => {
|
||||
page::update!(self.pages, message, desktop::Page);
|
||||
}
|
||||
|
|
@ -718,7 +723,7 @@ impl cosmic::Application for SettingsApp {
|
|||
// It is necessary to delay init to allow time for the page sender to be initialized
|
||||
Message::DelayedInit(active_id) => {
|
||||
if self.page_sender.is_none() {
|
||||
return cosmic::command::message(Message::DelayedInit(active_id));
|
||||
return cosmic::task::message(Message::DelayedInit(active_id));
|
||||
}
|
||||
|
||||
return self.activate_page(active_id);
|
||||
|
|
@ -832,7 +837,7 @@ impl SettingsApp {
|
|||
Task::batch(vec![
|
||||
leave_task,
|
||||
page_task,
|
||||
cosmic::command::future(async { Message::SetWindowTitle }),
|
||||
cosmic::task::future(async { Message::SetWindowTitle }),
|
||||
])
|
||||
}
|
||||
|
||||
|
|
@ -1005,7 +1010,7 @@ impl SettingsApp {
|
|||
if tasks.is_empty() {
|
||||
Task::none()
|
||||
} else {
|
||||
cosmic::command::batch(tasks)
|
||||
cosmic::task::batch(tasks)
|
||||
.map(Message::PageMessage)
|
||||
.map(Into::into)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ pub enum PageCommands {
|
|||
/// Date & Time settings page
|
||||
#[cfg(feature = "page-date")]
|
||||
DateTime,
|
||||
/// Default application associations
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
DefaultApps,
|
||||
/// Desktop settings page
|
||||
Desktop,
|
||||
/// Displays settings page
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> cosmic::Task<crate::pages::Message> {
|
||||
// TODO start stream for new device
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
match zbus::Connection::system().await {
|
||||
Ok(connection) => Message::DBusConnect(connection, sender),
|
||||
Err(why) => Message::DBusError(why.to_string()),
|
||||
|
|
@ -261,7 +261,7 @@ impl Page {
|
|||
Active::Disabling
|
||||
};
|
||||
self.update_status();
|
||||
return cosmic::command::future(change_adapter_status(
|
||||
return cosmic::task::future(change_adapter_status(
|
||||
connection.clone(),
|
||||
path,
|
||||
active,
|
||||
|
|
@ -276,7 +276,7 @@ impl Page {
|
|||
} else {
|
||||
Active::Disabling
|
||||
};
|
||||
cosmic::command::future(change_adapter_status(
|
||||
cosmic::task::future(change_adapter_status(
|
||||
connection.clone(),
|
||||
path.clone(),
|
||||
active,
|
||||
|
|
@ -284,7 +284,7 @@ impl Page {
|
|||
})
|
||||
.collect();
|
||||
self.update_status();
|
||||
return cosmic::command::batch(tasks);
|
||||
return cosmic::task::batch(tasks);
|
||||
}
|
||||
tracing::warn!("No DBus connection ready");
|
||||
}
|
||||
|
|
@ -306,7 +306,7 @@ impl Page {
|
|||
));
|
||||
}
|
||||
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
let result: zbus::Result<HashMap<OwnedObjectPath, Adapter>> = async {
|
||||
futures::future::join_all(
|
||||
bluez_zbus::get_adapters(&connection)
|
||||
|
|
@ -342,7 +342,7 @@ impl Page {
|
|||
self.update_status();
|
||||
|
||||
if self.selected_adapter.is_none() && self.adapters.len() == 1 {
|
||||
return cosmic::command::message(Message::SelectAdapter(
|
||||
return cosmic::task::message(Message::SelectAdapter(
|
||||
self.adapters.keys().next().cloned(),
|
||||
));
|
||||
}
|
||||
|
|
@ -365,7 +365,7 @@ impl Page {
|
|||
tracing::debug!("Adapter {} added", adapter.address);
|
||||
self.adapters.insert(path.clone(), adapter);
|
||||
if self.selected_adapter.is_none() {
|
||||
return cosmic::command::message(Message::SelectAdapter(Some(path)));
|
||||
return cosmic::task::message(Message::SelectAdapter(Some(path)));
|
||||
}
|
||||
}
|
||||
Message::UpdatedAdapter(path, update) => {
|
||||
|
|
@ -381,7 +381,7 @@ impl Page {
|
|||
&& existing.scanning == Active::Disabled =>
|
||||
{
|
||||
existing.scanning = Active::Enabling;
|
||||
return cosmic::command::future(start_discovery(connection, path));
|
||||
return cosmic::task::future(start_discovery(connection, path));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -412,19 +412,20 @@ impl Page {
|
|||
if let Some(connection) = self.connection.as_ref() {
|
||||
let connection = connection.clone();
|
||||
if let Some((path, adapter)) = self.get_selected_adapter_mut() {
|
||||
let mut fut: Vec<Task<Message>> = vec![cosmic::command::future(
|
||||
get_devices(connection.clone(), path.clone()),
|
||||
)];
|
||||
let mut fut: Vec<Task<Message>> = vec![cosmic::task::future(get_devices(
|
||||
connection.clone(),
|
||||
path.clone(),
|
||||
))];
|
||||
if adapter.enabled == Active::Enabled
|
||||
&& adapter.scanning == Active::Disabled
|
||||
{
|
||||
fut.push(cosmic::command::future(start_discovery(
|
||||
fut.push(cosmic::task::future(start_discovery(
|
||||
connection,
|
||||
path.clone(),
|
||||
)));
|
||||
}
|
||||
|
||||
return cosmic::command::batch(fut);
|
||||
return cosmic::task::batch(fut);
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No DBus connection ready");
|
||||
|
|
@ -440,7 +441,7 @@ impl Page {
|
|||
let connection = connection.clone();
|
||||
if let Some(device) = self.devices.get_mut(&path) {
|
||||
device.enabled = Active::Disabling;
|
||||
return cosmic::command::future(forget_device(connection, path.clone()));
|
||||
return cosmic::task::future(forget_device(connection, path.clone()));
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No DBus connection ready");
|
||||
|
|
@ -458,7 +459,7 @@ impl Page {
|
|||
return cosmic::Task::none();
|
||||
}
|
||||
device.enabled = Active::Enabling;
|
||||
return cosmic::command::future(connect_device(connection, path));
|
||||
return cosmic::task::future(connect_device(connection, path));
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No DBus connection ready");
|
||||
|
|
@ -474,7 +475,7 @@ impl Page {
|
|||
return cosmic::Task::none();
|
||||
}
|
||||
device.enabled = Active::Disabling;
|
||||
return cosmic::command::future(disconnect_device(connection, path));
|
||||
return cosmic::task::future(disconnect_device(connection, path));
|
||||
}
|
||||
} else {
|
||||
tracing::warn!("No DBus connection ready");
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use cosmic::iced_widget::scrollable::{Direction, Scrollbar};
|
|||
use cosmic::widget::icon::{from_name, icon};
|
||||
use cosmic::widget::{
|
||||
button, color_picker::ColorPickerUpdate, container, flex_row, horizontal_space, radio, row,
|
||||
scrollable, settings, spin_button, text, ColorPickerModel,
|
||||
scrollable, settings, text, ColorPickerModel,
|
||||
};
|
||||
use cosmic::{widget, Apply, Element, Task};
|
||||
#[cfg(feature = "wayland")]
|
||||
|
|
@ -387,7 +387,7 @@ impl Page {
|
|||
)
|
||||
),
|
||||
// Icon theme previews
|
||||
cosmic::widget::column::with_children(vec![
|
||||
widget::column::with_children(vec![
|
||||
text::heading(&*ICON_THEME).into(),
|
||||
flex_row(
|
||||
self.icon_themes
|
||||
|
|
@ -424,7 +424,7 @@ impl Page {
|
|||
self.context_view = Some(ContextView::MonospaceFont);
|
||||
self.font_search.clear();
|
||||
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("monospace-font").into(),
|
||||
));
|
||||
|
|
@ -434,7 +434,7 @@ impl Page {
|
|||
self.context_view = Some(ContextView::SystemFont);
|
||||
self.font_search.clear();
|
||||
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("interface-font").into(),
|
||||
));
|
||||
|
|
@ -781,7 +781,7 @@ impl Page {
|
|||
}
|
||||
|
||||
Message::Left => {
|
||||
tasks.push(cosmic::command::message(app::Message::SetTheme(
|
||||
tasks.push(cosmic::task::message(app::Message::SetTheme(
|
||||
cosmic::theme::system_preference(),
|
||||
)));
|
||||
}
|
||||
|
|
@ -865,7 +865,7 @@ impl Page {
|
|||
|
||||
#[cfg(feature = "ashpd")]
|
||||
Message::StartImport => {
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
let res = SelectedFiles::open_file()
|
||||
.modal(true)
|
||||
.filter(FileFilter::glob(FileFilter::new("ron"), "*.ron"))
|
||||
|
|
@ -888,7 +888,7 @@ impl Page {
|
|||
let is_dark = self.theme_mode.is_dark;
|
||||
let name = format!("{}.ron", if is_dark { fl!("dark") } else { fl!("light") });
|
||||
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
let res = SelectedFiles::save_file()
|
||||
.modal(true)
|
||||
.current_name(Some(name.as_str()))
|
||||
|
|
@ -918,7 +918,7 @@ impl Page {
|
|||
return Task::none();
|
||||
};
|
||||
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
let res = tokio::fs::read_to_string(path).await;
|
||||
if let Some(b) = res.ok().and_then(|s| ron::de::from_str(&s).ok()) {
|
||||
Message::ImportSuccess(Box::new(b))
|
||||
|
|
@ -944,7 +944,7 @@ impl Page {
|
|||
|
||||
let theme_builder = self.theme_builder.clone();
|
||||
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
let Ok(builder) =
|
||||
ron::ser::to_string_pretty(&theme_builder, PrettyConfig::default())
|
||||
else {
|
||||
|
|
@ -1062,7 +1062,7 @@ impl Page {
|
|||
|
||||
Message::IconsAndToolkit => {
|
||||
self.context_view = Some(ContextView::IconsAndToolkit);
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
"".into(),
|
||||
));
|
||||
|
|
@ -1080,7 +1080,7 @@ impl Page {
|
|||
let is_dark = self.theme_mode.is_dark;
|
||||
let current_theme = self.theme.clone();
|
||||
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
let config = if is_dark {
|
||||
Theme::dark_config()
|
||||
} else {
|
||||
|
|
@ -1180,7 +1180,7 @@ impl Page {
|
|||
let task = match message {
|
||||
ColorPickerUpdate::AppliedColor | ColorPickerUpdate::Reset => {
|
||||
needs_update = true;
|
||||
cosmic::command::message(crate::app::Message::CloseContextDrawer)
|
||||
cosmic::task::message(crate::app::Message::CloseContextDrawer)
|
||||
}
|
||||
|
||||
ColorPickerUpdate::ActionFinished => {
|
||||
|
|
@ -1189,12 +1189,12 @@ impl Page {
|
|||
}
|
||||
|
||||
ColorPickerUpdate::Cancel => {
|
||||
cosmic::command::message(crate::app::Message::CloseContextDrawer)
|
||||
cosmic::task::message(crate::app::Message::CloseContextDrawer)
|
||||
}
|
||||
|
||||
ColorPickerUpdate::ToggleColorPicker => {
|
||||
self.context_view = Some(context_view);
|
||||
cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
context_title,
|
||||
))
|
||||
|
|
@ -1436,11 +1436,11 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
&mut self,
|
||||
_sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> Task<crate::pages::Message> {
|
||||
let (task, handle) = cosmic::command::batch(vec![
|
||||
let (task, handle) = cosmic::task::batch(vec![
|
||||
// Load icon themes
|
||||
cosmic::command::future(icon_themes::fetch()).map(crate::pages::Message::Appearance),
|
||||
cosmic::task::future(icon_themes::fetch()).map(crate::pages::Message::Appearance),
|
||||
// Load font families
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let (mono, interface) = font_config::load_font_families();
|
||||
Message::FontConfig(font_config::Message::LoadedFonts(mono, interface))
|
||||
})
|
||||
|
|
@ -1457,7 +1457,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
handle.abort();
|
||||
}
|
||||
|
||||
cosmic::command::message(crate::pages::Message::Appearance(Message::Left))
|
||||
cosmic::task::message(crate::pages::Message::Appearance(Message::Left))
|
||||
}
|
||||
|
||||
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
|
||||
|
|
@ -1971,7 +1971,7 @@ pub fn window_management() -> Section<crate::pages::Message> {
|
|||
settings::section()
|
||||
.title(§ion.title)
|
||||
.add(settings::item::builder(&descriptions[active_hint]).control(
|
||||
cosmic::widget::spin_button(
|
||||
widget::spin_button(
|
||||
page.theme_builder.active_hint.to_string(),
|
||||
page.theme_builder.active_hint,
|
||||
1,
|
||||
|
|
@ -1980,16 +1980,16 @@ pub fn window_management() -> Section<crate::pages::Message> {
|
|||
Message::WindowHintSize,
|
||||
),
|
||||
))
|
||||
.add(settings::item::builder(&descriptions[gaps]).control(
|
||||
cosmic::widget::spin_button(
|
||||
.add(
|
||||
settings::item::builder(&descriptions[gaps]).control(widget::spin_button(
|
||||
page.theme_builder.gaps.1.to_string(),
|
||||
page.theme_builder.gaps.1,
|
||||
1,
|
||||
page.theme.active_hint,
|
||||
500,
|
||||
Message::GapSize,
|
||||
),
|
||||
))
|
||||
)),
|
||||
)
|
||||
.apply(Element::from)
|
||||
.map(crate::pages::Message::Appearance)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ impl Page {
|
|||
}
|
||||
Message::AddAppletDrawer => {
|
||||
self.context = Some(ContextDrawer::AddApplet);
|
||||
return cosmic::command::message(app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
Cow::Owned(fl!("add-applet")),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -577,7 +577,7 @@ impl Page {
|
|||
Category::Wallpapers => {
|
||||
if self.config.current_folder.is_some() {
|
||||
let _ = self.config.set_current_folder(None);
|
||||
task = cosmic::command::future(async move {
|
||||
task = cosmic::task::future(async move {
|
||||
let folder = change_folder(Config::default_folder().to_owned(), true).await;
|
||||
Message::ChangeFolder(folder)
|
||||
});
|
||||
|
|
@ -597,7 +597,7 @@ impl Page {
|
|||
tracing::error!(?path, ?why, "failed to set current folder");
|
||||
}
|
||||
|
||||
task = cosmic::command::future(async move {
|
||||
task = cosmic::task::future(async move {
|
||||
Message::ChangeFolder(change_folder(path, false).await)
|
||||
});
|
||||
}
|
||||
|
|
@ -605,7 +605,7 @@ impl Page {
|
|||
|
||||
Category::AddFolder => {
|
||||
#[cfg(feature = "xdg-portal")]
|
||||
return cosmic::command::future(async {
|
||||
return cosmic::task::future(async {
|
||||
let dialog_result = file_chooser::open::Dialog::new()
|
||||
.title(fl!("wallpaper", "folder-dialog"))
|
||||
.accept_label(fl!("dialog-add"))
|
||||
|
|
@ -751,7 +751,7 @@ impl Page {
|
|||
|
||||
Message::ColorAddContext => {
|
||||
self.context_view = Some(ContextView::AddColor);
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("color-picker").into(),
|
||||
));
|
||||
|
|
@ -972,20 +972,20 @@ impl Page {
|
|||
}
|
||||
|
||||
// Load preview images concurrently for each custom image stored in the on-disk config.
|
||||
return cosmic::command::batch(
|
||||
return cosmic::task::batch(
|
||||
self.config
|
||||
.custom_images()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|path| {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let result = wallpaper::load_image_with_thumbnail(path);
|
||||
|
||||
Message::ImageAdd(result.map(Arc::new))
|
||||
})
|
||||
})
|
||||
// Cache wallpaper preview early to prevent blank previews on reload
|
||||
.chain(std::iter::once(cosmic::command::message::<
|
||||
.chain(std::iter::once(cosmic::task::message::<
|
||||
_,
|
||||
crate::app::Message,
|
||||
>(
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
}));
|
||||
}
|
||||
|
||||
cosmic::command::future(on_enter())
|
||||
cosmic::task::future(on_enter())
|
||||
}
|
||||
|
||||
fn on_leave(&mut self) -> Task<crate::pages::Message> {
|
||||
|
|
@ -293,7 +293,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
&mut self,
|
||||
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> Task<crate::pages::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let mut randr = List::default();
|
||||
|
||||
let test_mode = randr.modes.insert(cosmic_randr_shell::Mode {
|
||||
|
|
@ -380,7 +380,7 @@ impl Page {
|
|||
tracing::error!(?why, "cosmic-randr error");
|
||||
} else {
|
||||
// Reload display info
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
crate::Message::PageMessage(on_enter().await)
|
||||
});
|
||||
}
|
||||
|
|
@ -406,11 +406,11 @@ impl Page {
|
|||
Message::DialogCountdown => {
|
||||
if self.dialog_countdown == 0 {
|
||||
if self.dialog.is_some() {
|
||||
return cosmic::command::message(app::Message::from(Message::DialogCancel));
|
||||
return cosmic::task::message(app::Message::from(Message::DialogCancel));
|
||||
}
|
||||
} else {
|
||||
self.dialog_countdown -= 1;
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
tokio::time::sleep(time::Duration::from_secs(1)).await;
|
||||
Message::DialogCountdown
|
||||
});
|
||||
|
|
@ -449,7 +449,7 @@ impl Page {
|
|||
//
|
||||
// Message::NightLightContext => {
|
||||
// self.context = Some(ContextDrawer::NightLight);
|
||||
// return cosmic::command::message(app::Message::OpenContextDrawer(
|
||||
// return cosmic::task::message(app::Message::OpenContextDrawer(
|
||||
// text::NIGHT_LIGHT.clone().into(),
|
||||
// ));
|
||||
// }
|
||||
|
|
@ -473,7 +473,7 @@ impl Page {
|
|||
Message::Position(display, x, y) => return self.set_position(display, x, y),
|
||||
|
||||
Message::Refresh => {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
crate::Message::PageMessage(on_enter().await)
|
||||
});
|
||||
}
|
||||
|
|
@ -583,7 +583,7 @@ impl Page {
|
|||
}
|
||||
self.dialog = Some(revert_request);
|
||||
self.dialog_countdown = 10;
|
||||
cosmic::command::future(async {
|
||||
cosmic::task::future(async {
|
||||
tokio::time::sleep(time::Duration::from_secs(1)).await;
|
||||
app::Message::from(Message::DialogCountdown)
|
||||
})
|
||||
|
|
@ -872,7 +872,7 @@ impl Page {
|
|||
|
||||
// Removes the dialog if no change is being made
|
||||
if Some(request) == self.dialog {
|
||||
tasks.push(cosmic::command::message(app::Message::from(
|
||||
tasks.push(cosmic::task::message(app::Message::from(
|
||||
Message::DialogComplete,
|
||||
)));
|
||||
}
|
||||
|
|
@ -970,7 +970,7 @@ impl Page {
|
|||
}
|
||||
}
|
||||
|
||||
tasks.push(cosmic::command::future(async move {
|
||||
tasks.push(cosmic::task::future(async move {
|
||||
tracing::debug!(?task, "executing");
|
||||
app::Message::from(Message::RandrResult(Arc::new(task.status().await)))
|
||||
}));
|
||||
|
|
|
|||
|
|
@ -460,7 +460,7 @@ impl Page {
|
|||
|
||||
Message::ShowInputSourcesContext => {
|
||||
self.context = Some(Context::ShowInputSourcesContext);
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("keyboard-sources", "add").into(),
|
||||
));
|
||||
|
|
@ -472,7 +472,7 @@ impl Page {
|
|||
|
||||
Message::OpenSpecialCharacterContext(key) => {
|
||||
self.context = Some(Context::SpecialCharacter(key));
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
key.title().into(),
|
||||
));
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ impl Model {
|
|||
self.shortcut_context = Some(id);
|
||||
self.replace_dialog = None;
|
||||
|
||||
let mut tasks = vec![cosmic::command::message(
|
||||
let mut tasks = vec![cosmic::task::message(
|
||||
crate::app::Message::OpenContextDrawer(self.entity, description.into()),
|
||||
)];
|
||||
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ impl Page {
|
|||
Message::ShortcutContext => {
|
||||
self.add_shortcut.enable();
|
||||
return Task::batch(vec![
|
||||
cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("custom-shortcuts", "context").into(),
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -221,28 +221,28 @@ impl Page {
|
|||
match message {
|
||||
Message::Category(category) => match category {
|
||||
Category::Custom => {
|
||||
cosmic::command::message(crate::app::Message::Page(self.sub_pages.custom))
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.custom))
|
||||
}
|
||||
|
||||
Category::ManageWindow => cosmic::command::message(crate::app::Message::Page(
|
||||
self.sub_pages.manage_window,
|
||||
)),
|
||||
Category::ManageWindow => {
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.manage_window))
|
||||
}
|
||||
|
||||
Category::MoveWindow => {
|
||||
cosmic::command::message(crate::app::Message::Page(self.sub_pages.move_window))
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.move_window))
|
||||
}
|
||||
|
||||
Category::Nav => {
|
||||
cosmic::command::message(crate::app::Message::Page(self.sub_pages.nav))
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.nav))
|
||||
}
|
||||
|
||||
Category::System => {
|
||||
cosmic::command::message(crate::app::Message::Page(self.sub_pages.system))
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.system))
|
||||
}
|
||||
|
||||
Category::WindowTiling => cosmic::command::message(crate::app::Message::Page(
|
||||
self.sub_pages.window_tiling,
|
||||
)),
|
||||
Category::WindowTiling => {
|
||||
cosmic::task::message(crate::app::Message::Page(self.sub_pages.window_tiling))
|
||||
}
|
||||
},
|
||||
|
||||
Message::Search(input) => {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ pub enum Message {
|
|||
CustomShortcuts(input::keyboard::shortcuts::custom::Message),
|
||||
#[cfg(feature = "page-date")]
|
||||
DateAndTime(time::date::Message),
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
DefaultApps(system::default_apps::Message),
|
||||
Desktop(desktop::Message),
|
||||
DesktopWallpaper(desktop::wallpaper::Message),
|
||||
#[cfg(feature = "page-workspaces")]
|
||||
|
|
|
|||
|
|
@ -289,10 +289,10 @@ impl Page {
|
|||
Message::OpenPage { page, device } => {
|
||||
let mut tasks = Vec::<Task<crate::app::Message>>::new();
|
||||
|
||||
tasks.push(cosmic::command::message(crate::app::Message::Page(page)));
|
||||
tasks.push(cosmic::task::message(crate::app::Message::Page(page)));
|
||||
|
||||
if let Some(device) = device {
|
||||
tasks.push(cosmic::command::message(crate::app::Message::PageMessage(
|
||||
tasks.push(cosmic::task::message(crate::app::Message::PageMessage(
|
||||
match device {
|
||||
DeviceVariant::WiFi(device) => {
|
||||
crate::pages::Message::WiFi(wifi::Message::SelectDevice(device))
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> cosmic::Task<crate::pages::Message> {
|
||||
if self.nm_task.is_none() {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
zbus::Connection::system()
|
||||
.await
|
||||
.context("failed to create system dbus connection")
|
||||
|
|
@ -455,7 +455,7 @@ impl Page {
|
|||
|
||||
Message::WireGuardConfig => {
|
||||
if let Some(VpnDialog::WireGuardName(device, filename, path)) = self.dialog.take() {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
let new_path = path.replace(&filename, &device);
|
||||
_ = std::fs::rename(&path, &new_path);
|
||||
match super::nm_add_vpn_file("wireguard", new_path).await {
|
||||
|
|
@ -474,7 +474,7 @@ impl Page {
|
|||
ConnectionSettings::Vpn(ref settings) => settings,
|
||||
ConnectionSettings::Wireguard { id } => {
|
||||
let connection_name = id.clone();
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
if let Err(why) = nmcli::connect(&connection_name).await {
|
||||
return Message::Error(
|
||||
ErrorKind::Connect,
|
||||
|
|
@ -501,7 +501,7 @@ impl Page {
|
|||
|
||||
_ => {
|
||||
let connection_name = settings.id.clone();
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
if let Err(why) = nmcli::connect(&connection_name).await {
|
||||
return Message::Error(
|
||||
ErrorKind::Connect,
|
||||
|
|
@ -546,7 +546,7 @@ impl Page {
|
|||
Message::Settings(uuid) => {
|
||||
self.close_popup_and_apply_updates();
|
||||
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
super::nm_edit_connection(uuid.as_ref())
|
||||
.then(|res| async move {
|
||||
match res {
|
||||
|
|
@ -639,7 +639,7 @@ impl Page {
|
|||
username: String,
|
||||
password: SecureString,
|
||||
) -> Task<Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
if let Err(why) = nmcli::set_username(&connection_name, &username).await {
|
||||
return Message::Error(ErrorKind::WithPassword("username"), why.to_string());
|
||||
}
|
||||
|
|
@ -866,7 +866,7 @@ fn popup_button(message: Message, text: &str) -> Element<'_, Message> {
|
|||
}
|
||||
|
||||
fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
match NetworkManagerState::new(&conn).await {
|
||||
Ok(state) => Message::UpdateState(state),
|
||||
Err(why) => Message::Error(ErrorKind::UpdatingState, why.to_string()),
|
||||
|
|
@ -875,7 +875,7 @@ fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
}
|
||||
|
||||
fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let filter =
|
||||
|device_type| matches!(device_type, network_manager::devices::DeviceType::WireGuard);
|
||||
|
||||
|
|
@ -938,7 +938,7 @@ fn add_network() -> Task<crate::app::Message> {
|
|||
}
|
||||
}
|
||||
})
|
||||
.apply(cosmic::command::future)
|
||||
.apply(cosmic::task::future)
|
||||
}
|
||||
|
||||
fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
|
|
@ -1040,7 +1040,7 @@ fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
Ok::<_, zbus::Error>(settings)
|
||||
};
|
||||
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
settings.await.map_or_else(
|
||||
|why| Message::Error(ErrorKind::ConnectionSettings, why.to_string()),
|
||||
Message::KnownConnections,
|
||||
|
|
|
|||
|
|
@ -776,7 +776,7 @@ fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
Ok::<_, zbus::Error>(settings)
|
||||
};
|
||||
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
settings
|
||||
.await
|
||||
.context("failed to get connection settings")
|
||||
|
|
@ -789,7 +789,7 @@ fn connection_settings(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
}
|
||||
|
||||
pub fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
match NetworkManagerState::new(&conn).await {
|
||||
Ok(state) => Message::UpdateState(state),
|
||||
Err(why) => Message::Error(why.to_string()),
|
||||
|
|
@ -798,7 +798,7 @@ pub fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
}
|
||||
|
||||
pub fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let filter =
|
||||
|device_type| matches!(device_type, network_manager::devices::DeviceType::Wifi);
|
||||
match network_manager::devices::list(&conn, filter).await {
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||
) -> cosmic::Task<crate::pages::Message> {
|
||||
if self.nm_task.is_none() {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
zbus::Connection::system()
|
||||
.await
|
||||
.context("failed to create system dbus connection")
|
||||
|
|
@ -265,7 +265,7 @@ impl Page {
|
|||
Message::NetworkManager(_event) => (),
|
||||
|
||||
Message::AddNetwork => {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
_ = super::nm_add_wired().await;
|
||||
// TODO: Update when iced is rebased to use then method.
|
||||
Message::Refresh
|
||||
|
|
@ -332,7 +332,7 @@ impl Page {
|
|||
Message::Settings(uuid) => {
|
||||
self.close_popup_and_apply_updates();
|
||||
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
_ = super::nm_edit_connection(uuid.as_ref()).await;
|
||||
// TODO: Update when iced is rebased to use then method.
|
||||
Message::Refresh
|
||||
|
|
@ -618,7 +618,7 @@ fn popup_button(message: Message, text: &str) -> Element<'_, Message> {
|
|||
}
|
||||
|
||||
fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
match NetworkManagerState::new(&conn).await {
|
||||
Ok(state) => Message::UpdateState(state),
|
||||
Err(why) => Message::Error(why.to_string()),
|
||||
|
|
@ -627,7 +627,7 @@ fn update_state(conn: zbus::Connection) -> Task<crate::app::Message> {
|
|||
}
|
||||
|
||||
fn update_devices(conn: zbus::Connection) -> Task<crate::app::Message> {
|
||||
cosmic::command::future(async move {
|
||||
cosmic::task::future(async move {
|
||||
let filter =
|
||||
|device_type| matches!(device_type, network_manager::devices::DeviceType::Ethernet);
|
||||
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ impl Page {
|
|||
|
||||
let mut command = None;
|
||||
if let Some(&node_id) = self.source_ids.get(self.active_source.unwrap_or(0)) {
|
||||
command = Some(cosmic::command::future(async move {
|
||||
command = Some(cosmic::task::future(async move {
|
||||
tokio::time::sleep(Duration::from_millis(64)).await;
|
||||
crate::pages::Message::Sound(Message::SourceVolumeApply(node_id))
|
||||
}));
|
||||
|
|
@ -338,7 +338,7 @@ impl Page {
|
|||
|
||||
let mut command = None;
|
||||
if let Some(&node_id) = self.sink_ids.get(self.active_sink.unwrap_or(0)) {
|
||||
command = Some(cosmic::command::future(async move {
|
||||
command = Some(cosmic::task::future(async move {
|
||||
tokio::time::sleep(Duration::from_millis(64)).await;
|
||||
crate::pages::Message::Sound(Message::SinkVolumeApply(node_id))
|
||||
}));
|
||||
|
|
@ -542,7 +542,7 @@ impl Page {
|
|||
.insert(device_id.clone(), Some(profile.clone()));
|
||||
|
||||
self.changing_sink_profile = true;
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
pactl_set_card_profile(name, profile).await;
|
||||
Message::SinkProfileSelect(device_id)
|
||||
})
|
||||
|
|
@ -574,7 +574,7 @@ impl Page {
|
|||
.insert(device_id.clone(), Some(profile.clone()));
|
||||
|
||||
self.changing_source_profile = true;
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
pactl_set_card_profile(name, profile).await;
|
||||
Message::SourceProfileSelect(device_id)
|
||||
})
|
||||
|
|
|
|||
414
cosmic-settings/src/pages/system/default_apps.rs
Normal file
414
cosmic-settings/src/pages/system/default_apps.rs
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
// Copyright 2024 System76 <info@system76.com>
|
||||
// Copyright 2024 bbb651 <bar.ye651@gmail.com>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use cosmic::{
|
||||
widget::{self, dropdown, icon, settings},
|
||||
Apply, Element, Task,
|
||||
};
|
||||
use cosmic_settings_page::{self as page, section, Section};
|
||||
use mime_apps::App;
|
||||
use slotmap::SlotMap;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
const DROPDOWN_WEB_BROWSER: usize = 0;
|
||||
const DROPDOWN_FILE_MANAGER: usize = 1;
|
||||
const DROPDOWN_MAIL: usize = 2;
|
||||
const DROPDOWN_MUSIC: usize = 3;
|
||||
const DROPDOWN_VIDEO: usize = 4;
|
||||
const DROPDOWN_PHOTO: usize = 5;
|
||||
const DROPDOWN_CALENDAR: usize = 6;
|
||||
// const DROPDOWN_TERMINAL: usize = 7;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Category {
|
||||
Audio,
|
||||
Calendar,
|
||||
FileManager,
|
||||
Image,
|
||||
Mail,
|
||||
Mime(&'static str),
|
||||
// Terminal,
|
||||
Video,
|
||||
WebBrowser,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Message {
|
||||
SetDefault(Category, usize),
|
||||
Update(CachedMimeApps),
|
||||
}
|
||||
|
||||
impl From<Message> for crate::app::Message {
|
||||
fn from(message: Message) -> Self {
|
||||
crate::pages::Message::DefaultApps(message).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Message> for crate::pages::Message {
|
||||
fn from(message: Message) -> Self {
|
||||
crate::pages::Message::DefaultApps(message)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CachedMimeApps {
|
||||
pub list: mime_apps::List,
|
||||
pub local_list: mime_apps::List,
|
||||
pub apps: Vec<AppMeta>,
|
||||
pub known_mimes: BTreeSet<mime::Mime>,
|
||||
pub config_path: Box<Path>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppMeta {
|
||||
selected: Option<usize>,
|
||||
app_ids: Vec<String>,
|
||||
apps: Vec<String>,
|
||||
icons: Vec<icon::Handle>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Page {
|
||||
on_enter_handle: Option<cosmic::iced::task::Handle>,
|
||||
mime_apps: Option<CachedMimeApps>,
|
||||
}
|
||||
|
||||
impl page::AutoBind<crate::pages::Message> for Page {}
|
||||
|
||||
impl page::Page<crate::pages::Message> for Page {
|
||||
fn content(
|
||||
&self,
|
||||
sections: &mut SlotMap<section::Entity, Section<crate::pages::Message>>,
|
||||
) -> Option<cosmic_settings_page::Content> {
|
||||
Some(vec![sections.insert(apps())])
|
||||
}
|
||||
|
||||
fn info(&self) -> page::Info {
|
||||
page::Info::new("default-apps", "application-default-symbolic")
|
||||
.title(fl!("default-apps"))
|
||||
.description(fl!("default-apps", "desc"))
|
||||
}
|
||||
|
||||
fn on_enter(
|
||||
&mut self,
|
||||
_sender: mpsc::Sender<crate::pages::Message>,
|
||||
) -> Task<crate::pages::Message> {
|
||||
let (task, on_enter_handle) = Task::future(async move {
|
||||
let mut list = mime_apps::List::default();
|
||||
list.load_from_paths(&mime_apps::list_paths());
|
||||
|
||||
let mut local_list = mime_apps::List::default();
|
||||
|
||||
if let Some(path) = mime_apps::local_list_path() {
|
||||
if let Ok(buffer) = std::fs::read_to_string(&path) {
|
||||
local_list.load_from(&buffer);
|
||||
}
|
||||
}
|
||||
|
||||
let assocs = mime_apps::associations::by_app();
|
||||
|
||||
let apps = vec![
|
||||
load_defaults(&assocs, "x-scheme-handler/http").await,
|
||||
load_defaults(&assocs, "inode/directory").await,
|
||||
load_defaults(&assocs, "x-scheme-handler/mailto").await,
|
||||
load_defaults(&assocs, "audio/mp3").await,
|
||||
load_defaults(&assocs, "video/mp4").await,
|
||||
load_defaults(&assocs, "image/png").await,
|
||||
load_defaults(&assocs, "text/calendar").await,
|
||||
AppMeta {
|
||||
selected: None,
|
||||
app_ids: Vec::new(),
|
||||
apps: Vec::new(),
|
||||
icons: Vec::new(),
|
||||
},
|
||||
];
|
||||
|
||||
Message::Update(CachedMimeApps {
|
||||
apps,
|
||||
list,
|
||||
local_list,
|
||||
known_mimes: mime_apps::mime_info::mime_types(),
|
||||
config_path: dirs::config_dir()
|
||||
.expect("config dir not found")
|
||||
.join("mimeapps.list")
|
||||
.into(),
|
||||
})
|
||||
.into()
|
||||
})
|
||||
.abortable();
|
||||
|
||||
self.on_enter_handle = Some(on_enter_handle);
|
||||
|
||||
task
|
||||
}
|
||||
|
||||
fn on_leave(&mut self) -> Task<crate::pages::Message> {
|
||||
if let Some(handle) = self.on_enter_handle.take() {
|
||||
handle.abort();
|
||||
}
|
||||
|
||||
self.mime_apps = None;
|
||||
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn update(&mut self, message: Message) -> Task<crate::Message> {
|
||||
match message {
|
||||
Message::SetDefault(category, id) => {
|
||||
let Some(mime_apps) = self.mime_apps.as_mut() else {
|
||||
return Task::none();
|
||||
};
|
||||
|
||||
let mime_types: Vec<&str>;
|
||||
let (category_id, mime_types): (usize, &[&str]) = match category {
|
||||
Category::Audio => (DROPDOWN_MUSIC, {
|
||||
mime_types = mime_apps
|
||||
.known_mimes
|
||||
.iter()
|
||||
.map(|m| m.essence_str())
|
||||
.filter(|m| m.starts_with("audio"))
|
||||
.chain(
|
||||
[
|
||||
"application/ogg",
|
||||
"application/x-cue",
|
||||
"application/x-ogg",
|
||||
"x-content/audio-cdda",
|
||||
]
|
||||
.into_iter(),
|
||||
)
|
||||
.collect();
|
||||
&mime_types
|
||||
}),
|
||||
Category::Calendar => (DROPDOWN_CALENDAR, &["text/calendar"]),
|
||||
Category::FileManager => (DROPDOWN_FILE_MANAGER, &["inode/directory"]),
|
||||
Category::Image => (DROPDOWN_PHOTO, {
|
||||
mime_types = mime_apps
|
||||
.known_mimes
|
||||
.iter()
|
||||
.map(|m| m.essence_str())
|
||||
.filter(|m| m.starts_with("image"))
|
||||
.collect();
|
||||
&mime_types
|
||||
}),
|
||||
Category::Mail => (DROPDOWN_MAIL, &["x-scheme-handler/mailto"]),
|
||||
// Category::Terminal => (DROPDOWN_TERMINAL, &[]),
|
||||
Category::Video => (DROPDOWN_VIDEO, {
|
||||
mime_types = mime_apps
|
||||
.known_mimes
|
||||
.iter()
|
||||
.map(|m| m.essence_str())
|
||||
.filter(|m| m.starts_with("video"))
|
||||
.collect();
|
||||
&mime_types
|
||||
}),
|
||||
Category::WebBrowser => (
|
||||
DROPDOWN_WEB_BROWSER,
|
||||
&[
|
||||
"text/html",
|
||||
"application/xhtml+xml",
|
||||
"x-scheme-handler/chrome",
|
||||
"x-scheme-handler/http",
|
||||
"x-scheme-handler/https",
|
||||
],
|
||||
),
|
||||
Category::Mime(_mime_type) => return Task::none(),
|
||||
};
|
||||
|
||||
let meta = &mut mime_apps.apps[category_id];
|
||||
|
||||
if meta.selected != Some(id) {
|
||||
meta.selected = Some(id);
|
||||
let appid = &meta.app_ids[id];
|
||||
for mime in mime_types {
|
||||
if let Ok(mime) = mime.parse() {
|
||||
mime_apps
|
||||
.local_list
|
||||
.set_default_app(mime, [appid, ".desktop"].concat());
|
||||
};
|
||||
}
|
||||
|
||||
let mut buffer = mime_apps.local_list.to_string();
|
||||
buffer.push('\n');
|
||||
|
||||
_ = std::fs::write(&mime_apps.config_path, buffer);
|
||||
_ = std::process::Command::new("update-desktop-database").status();
|
||||
}
|
||||
}
|
||||
|
||||
Message::Update(mime_apps) => {
|
||||
self.mime_apps = Some(mime_apps);
|
||||
}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn apps() -> Section<crate::pages::Message> {
|
||||
Section::default().view::<Page>(move |_binder, page, section| {
|
||||
let Some(mime_apps) = page.mime_apps.as_ref() else {
|
||||
return widget::row().into();
|
||||
};
|
||||
|
||||
settings::section()
|
||||
.title(§ion.title)
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_WEB_BROWSER];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "web-browser"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::WebBrowser, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
.min_item_width(300.0)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_FILE_MANAGER];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "file-manager"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::FileManager, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_MAIL];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "mail-client"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::Mail, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_MUSIC];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "music"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::Audio, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_VIDEO];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "video"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::Video, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_PHOTO];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "photos"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::Image, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
.add({
|
||||
let meta = &mime_apps.apps[DROPDOWN_CALENDAR];
|
||||
settings::flex_item(
|
||||
fl!("default-apps", "calendar"),
|
||||
dropdown(&meta.apps, meta.selected, |id| {
|
||||
Message::SetDefault(Category::Calendar, id)
|
||||
})
|
||||
.icons(&meta.icons),
|
||||
)
|
||||
})
|
||||
// TODO: Decide on a mechanism for getting and setting the default terminal.
|
||||
// .add({
|
||||
// let meta = &mime_apps.apps[DROPDOWN_TERMINAL];
|
||||
// settings::flex_item(
|
||||
// fl!("default-apps", "terminal"),
|
||||
// dropdown(&meta.apps, meta.selected, |id| {
|
||||
// Message::SetDefault(Category::Terminal, id)
|
||||
// })
|
||||
// .icons(&meta.icons),
|
||||
// )
|
||||
// })
|
||||
.apply(Element::from)
|
||||
.map(crate::pages::Message::DefaultApps)
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_defaults(assocs: &BTreeMap<Arc<str>, Arc<App>>, for_mime: &str) -> AppMeta {
|
||||
let Ok(mime) = for_mime.parse() else {
|
||||
return AppMeta {
|
||||
selected: None,
|
||||
app_ids: Vec::new(),
|
||||
apps: Vec::new(),
|
||||
icons: Vec::new(),
|
||||
};
|
||||
};
|
||||
|
||||
let current_app_entry = xdg_mime_query_default(for_mime).await;
|
||||
let current_appid = current_app_entry
|
||||
.as_ref()
|
||||
.and_then(|entry| entry.strip_suffix(".desktop"));
|
||||
|
||||
let current_app = current_appid.and_then(|appid| assocs.get(appid));
|
||||
|
||||
let mut unsorted = mime_apps::apps_for_mime(&mime, assocs).collect::<Vec<_>>();
|
||||
unsorted.sort_unstable_by_key(|(_, app)| &app.name);
|
||||
|
||||
let mut selected = None;
|
||||
let mut app_ids = Vec::new();
|
||||
let mut apps = Vec::new();
|
||||
let mut icons = Vec::new();
|
||||
|
||||
for (id, (appid, app)) in unsorted.iter().enumerate() {
|
||||
if let Some(current_app) = current_app {
|
||||
if app.name.as_ref() == current_app.name.as_ref() {
|
||||
selected = Some(id);
|
||||
}
|
||||
}
|
||||
|
||||
app_ids.push(appid.as_ref().into());
|
||||
apps.push(app.name.as_ref().into());
|
||||
icons.push(if app.icon.starts_with('/') {
|
||||
icon::from_path(PathBuf::from(app.icon.as_ref()))
|
||||
} else {
|
||||
icon::from_name(app.icon.as_ref()).size(20).handle()
|
||||
});
|
||||
}
|
||||
|
||||
AppMeta {
|
||||
selected,
|
||||
app_ids,
|
||||
apps,
|
||||
icons,
|
||||
}
|
||||
}
|
||||
|
||||
async fn xdg_mime_query_default(mime_type: &str) -> Option<String> {
|
||||
let output = tokio::process::Command::new("xdg-mime")
|
||||
.args(&["query", "default", mime_type])
|
||||
.output()
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
String::from_utf8(output.stdout)
|
||||
.ok()
|
||||
.map(|string| string.trim().to_owned())
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#[cfg(feature = "page-about")]
|
||||
pub mod about;
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
pub mod default_apps;
|
||||
pub mod firmware;
|
||||
pub mod users;
|
||||
|
||||
|
|
@ -33,6 +35,10 @@ impl page::AutoBind<crate::pages::Message> for Page {
|
|||
page = page.sub_page::<about::Page>();
|
||||
}
|
||||
page = page.sub_page::<firmware::Page>();
|
||||
#[cfg(feature = "page-default-apps")]
|
||||
{
|
||||
page = page.sub_page::<default_apps::Page>();
|
||||
}
|
||||
page
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ impl Page {
|
|||
Message::TimezoneContext => {
|
||||
self.timezone_search.clear();
|
||||
self.timezone_context = true;
|
||||
return cosmic::command::message(crate::app::Message::OpenContextDrawer(
|
||||
return cosmic::task::message(crate::app::Message::OpenContextDrawer(
|
||||
self.entity,
|
||||
fl!("time-zone").into(),
|
||||
));
|
||||
|
|
@ -262,14 +262,14 @@ impl Page {
|
|||
Message::Error(why) => {
|
||||
tracing::error!(why, "failed to set timezone");
|
||||
self.timezone_context = false;
|
||||
return cosmic::command::message(crate::Message::CloseContextDrawer);
|
||||
return cosmic::task::message(crate::Message::CloseContextDrawer);
|
||||
}
|
||||
|
||||
Message::UpdateTime => {
|
||||
self.set_ntp(true);
|
||||
self.update_local_time();
|
||||
self.timezone_context = false;
|
||||
return cosmic::command::message(crate::Message::CloseContextDrawer);
|
||||
return cosmic::task::message(crate::Message::CloseContextDrawer);
|
||||
}
|
||||
|
||||
Message::Refresh(info) => {
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ impl page::Page<crate::pages::Message> for Page {
|
|||
&mut self,
|
||||
_sender: mpsc::Sender<crate::pages::Message>,
|
||||
) -> cosmic::Task<crate::pages::Message> {
|
||||
cosmic::command::future(async move { Message::Refresh(Arc::new(page_reload().await)) })
|
||||
cosmic::task::future(async move { Message::Refresh(Arc::new(page_reload().await)) })
|
||||
}
|
||||
|
||||
fn context_drawer(&self) -> Option<Element<'_, crate::pages::Message>> {
|
||||
|
|
@ -175,7 +175,7 @@ impl Page {
|
|||
let lang = language.lang_code.clone();
|
||||
let region = region.lang_code.clone();
|
||||
|
||||
commands.push(cosmic::command::future(async move {
|
||||
commands.push(cosmic::task::future(async move {
|
||||
_ = set_locale(lang, region).await;
|
||||
Message::Refresh(Arc::new(page_reload().await))
|
||||
}));
|
||||
|
|
@ -206,7 +206,7 @@ impl Page {
|
|||
}
|
||||
|
||||
Message::InstallAdditionalLanguages => {
|
||||
return cosmic::command::future(async move {
|
||||
return cosmic::task::future(async move {
|
||||
_ = tokio::process::Command::new("gnome-language-selector")
|
||||
.status()
|
||||
.await;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue