add search field in 'open with' dialog (#1943)
The "Open With" context menu item, which allows you to open a file, now includes a search option by application name. Demo: https://github.com/user-attachments/assets/b4ff5da3-4bdf-4a0f-a20b-b6783f293fef - [x] I have disclosed use of any AI generated code in my commit messages. - If you are using an LLM, and do not fully understand the changes it is making to the code base, do not create a PR. - In our experience, AI generated code often results in overly complex code that lacks enough context for a proper fix or feature inclusion. This results in considerably longer code reviews. Due to this, AI authored or partially authored PRs may be closed without comment. - [x] I understand these changes in full and will be able to respond to review comments. - [x] My change is accurately described in the commit message. - [x] My contribution is tested and working as described. - [x] I have read the [Developer Certificate of Origin](https://developercertificate.org/) and certify my contribution under its conditions.
This commit is contained in:
commit
3aa55b07b7
3 changed files with 67 additions and 17 deletions
|
|
@ -293,6 +293,7 @@ unknown-folder = unknown folder
|
||||||
## Open with
|
## Open with
|
||||||
menu-open-with = Open with...
|
menu-open-with = Open with...
|
||||||
default-app = {$name} (default)
|
default-app = {$name} (default)
|
||||||
|
search-application = Search by app name
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
show-details = Show details
|
show-details = Show details
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ unknown-folder = неизвестная папка
|
||||||
|
|
||||||
menu-open-with = Открыть с помощью…
|
menu-open-with = Открыть с помощью…
|
||||||
default-app = { $name } (по умолчанию)
|
default-app = { $name } (по умолчанию)
|
||||||
|
search-application = Поиск по названию приложения
|
||||||
|
|
||||||
## Show details
|
## Show details
|
||||||
|
|
||||||
|
|
|
||||||
82
src/app.rs
82
src/app.rs
|
|
@ -37,7 +37,7 @@ use cosmic::widget::menu::action::MenuAction;
|
||||||
use cosmic::widget::menu::key_bind::KeyBind;
|
use cosmic::widget::menu::key_bind::KeyBind;
|
||||||
use cosmic::widget::segmented_button::{self, Entity, ReorderEvent};
|
use cosmic::widget::segmented_button::{self, Entity, ReorderEvent};
|
||||||
use cosmic::widget::{self, icon, settings, space};
|
use cosmic::widget::{self, icon, settings, space};
|
||||||
use cosmic::{Application, ApplicationExt, Element, cosmic_theme, executor, style, surface, theme};
|
use cosmic::{Application, ApplicationExt, Element, cosmic_theme, executor, surface, theme};
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use notify_debouncer_full::notify::{self, RecommendedWatcher};
|
use notify_debouncer_full::notify::{self, RecommendedWatcher};
|
||||||
use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache, new_debouncer};
|
use notify_debouncer_full::{DebouncedEvent, Debouncer, RecommendedCache, new_debouncer};
|
||||||
|
|
@ -395,6 +395,7 @@ pub enum Message {
|
||||||
OpenWithBrowse,
|
OpenWithBrowse,
|
||||||
OpenWithDialog(Option<Entity>),
|
OpenWithDialog(Option<Entity>),
|
||||||
OpenWithSelection(usize),
|
OpenWithSelection(usize),
|
||||||
|
OpenWithSearchClear,
|
||||||
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
#[cfg(all(feature = "wayland", feature = "desktop-applet"))]
|
||||||
Overlap(window::Id, OverlapNotifyEvent),
|
Overlap(window::Id, OverlapNotifyEvent),
|
||||||
Paste(Option<Entity>),
|
Paste(Option<Entity>),
|
||||||
|
|
@ -566,6 +567,7 @@ pub enum DialogPage {
|
||||||
mime: mime_guess::Mime,
|
mime: mime_guess::Mime,
|
||||||
selected: usize,
|
selected: usize,
|
||||||
store_opt: Option<Arc<MimeApp>>,
|
store_opt: Option<Arc<MimeApp>>,
|
||||||
|
search_app_name: String,
|
||||||
},
|
},
|
||||||
PermanentlyDelete {
|
PermanentlyDelete {
|
||||||
paths: Box<[PathBuf]>,
|
paths: Box<[PathBuf]>,
|
||||||
|
|
@ -3209,9 +3211,17 @@ impl Application for App {
|
||||||
path,
|
path,
|
||||||
mime,
|
mime,
|
||||||
selected,
|
selected,
|
||||||
|
search_app_name,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let available_apps = self.mime_app_cache.get_apps_for_mime(&mime, true);
|
let mut available_apps =
|
||||||
|
self.mime_app_cache.get_apps_for_mime(&mime, true);
|
||||||
|
available_apps.retain(|(app, _)| {
|
||||||
|
app.name
|
||||||
|
.to_lowercase()
|
||||||
|
.trim()
|
||||||
|
.contains(search_app_name.to_lowercase().as_str().trim())
|
||||||
|
});
|
||||||
|
|
||||||
if let Some((app, _)) = available_apps.get(selected) {
|
if let Some((app, _)) = available_apps.get(selected) {
|
||||||
if let Some(mut command) =
|
if let Some(mut command) =
|
||||||
|
|
@ -3838,20 +3848,24 @@ impl Application for App {
|
||||||
let Some(path) = item.path_opt() else {
|
let Some(path) = item.path_opt() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
return self.push_dialog(
|
return Task::batch([
|
||||||
DialogPage::OpenWith {
|
self.push_dialog(
|
||||||
path: path.clone(),
|
DialogPage::OpenWith {
|
||||||
mime: item.mime.clone(),
|
path: path.clone(),
|
||||||
selected: 0,
|
mime: item.mime.clone(),
|
||||||
store_opt: "x-scheme-handler/mime"
|
selected: 0,
|
||||||
.parse::<mime_guess::Mime>()
|
store_opt: "x-scheme-handler/mime"
|
||||||
.ok()
|
.parse::<mime_guess::Mime>()
|
||||||
.and_then(|mime| {
|
.ok()
|
||||||
self.mime_app_cache.get(&mime).first().cloned()
|
.and_then(|mime| {
|
||||||
}),
|
self.mime_app_cache.get(&mime).first().cloned()
|
||||||
},
|
}),
|
||||||
Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()),
|
search_app_name: String::new(),
|
||||||
);
|
},
|
||||||
|
Some(CONFIRM_OPEN_WITH_BUTTON_ID.clone()),
|
||||||
|
),
|
||||||
|
widget::text_input::focus(self.dialog_text_input.clone()),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3860,6 +3874,14 @@ impl Application for App {
|
||||||
*selected = index;
|
*selected = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Message::OpenWithSearchClear => {
|
||||||
|
if let Some(DialogPage::OpenWith {
|
||||||
|
search_app_name, ..
|
||||||
|
}) = self.dialog_pages.front_mut()
|
||||||
|
{
|
||||||
|
*search_app_name = String::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::Paste(entity_opt) => {
|
Message::Paste(entity_opt) => {
|
||||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity)
|
||||||
|
|
@ -5114,6 +5136,7 @@ impl Application for App {
|
||||||
.and_then(|mime| {
|
.and_then(|mime| {
|
||||||
self.mime_app_cache.get(&mime).first().cloned()
|
self.mime_app_cache.get(&mime).first().cloned()
|
||||||
}),
|
}),
|
||||||
|
search_app_name: String::new(),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
@ -5970,6 +5993,7 @@ impl Application for App {
|
||||||
mime,
|
mime,
|
||||||
selected,
|
selected,
|
||||||
store_opt,
|
store_opt,
|
||||||
|
search_app_name,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let name = match path.file_name() {
|
let name = match path.file_name() {
|
||||||
|
|
@ -5978,7 +6002,13 @@ impl Application for App {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut column = widget::list_column();
|
let mut column = widget::list_column();
|
||||||
let available_apps = self.mime_app_cache.get_apps_for_mime(mime, true);
|
let mut available_apps = self.mime_app_cache.get_apps_for_mime(mime, true);
|
||||||
|
available_apps.retain(|(app, _)| {
|
||||||
|
app.name
|
||||||
|
.to_lowercase()
|
||||||
|
.trim()
|
||||||
|
.contains(search_app_name.to_lowercase().as_str().trim())
|
||||||
|
});
|
||||||
let item_height = 32.0;
|
let item_height = 32.0;
|
||||||
let mut displayed_default = false;
|
let mut displayed_default = false;
|
||||||
let mut last_kind = MimeAppMatch::Exact;
|
let mut last_kind = MimeAppMatch::Exact;
|
||||||
|
|
@ -6042,6 +6072,24 @@ impl Application for App {
|
||||||
.secondary_action(
|
.secondary_action(
|
||||||
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
|
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
|
||||||
)
|
)
|
||||||
|
.control(
|
||||||
|
widget::text_input::search_input(
|
||||||
|
fl!("search-application"),
|
||||||
|
search_app_name,
|
||||||
|
)
|
||||||
|
.id(self.dialog_text_input.clone())
|
||||||
|
.on_clear(Message::OpenWithSearchClear)
|
||||||
|
.on_input(move |search_app_name| {
|
||||||
|
Message::DialogUpdate(DialogPage::OpenWith {
|
||||||
|
path: path.clone(),
|
||||||
|
mime: mime.clone(),
|
||||||
|
selected: *selected,
|
||||||
|
store_opt: store_opt.clone(),
|
||||||
|
search_app_name,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_submit(|_| Message::DialogComplete),
|
||||||
|
)
|
||||||
.control(widget::scrollable(column).height({
|
.control(widget::scrollable(column).height({
|
||||||
let max_size = self
|
let max_size = self
|
||||||
.size
|
.size
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue