Type to search or seek (#859)
* WIP: type to search/seek * Implement type to seek
This commit is contained in:
parent
7874f96ef1
commit
f95762bd44
4 changed files with 111 additions and 27 deletions
|
|
@ -245,6 +245,11 @@ match-desktop = Match desktop
|
||||||
dark = Dark
|
dark = Dark
|
||||||
light = Light
|
light = Light
|
||||||
|
|
||||||
|
### Type to Search
|
||||||
|
type-to-search = Type to Search
|
||||||
|
type-to-search-recursive = Searches the current folder and all sub-folders
|
||||||
|
type-to-search-enter-path = Enters the path to the directory or file
|
||||||
|
|
||||||
# Context menu
|
# Context menu
|
||||||
add-to-sidebar = Add to sidebar
|
add-to-sidebar = Add to sidebar
|
||||||
compress = Compress
|
compress = Compress
|
||||||
|
|
|
||||||
111
src/app.rs
111
src/app.rs
|
|
@ -21,6 +21,7 @@ use cosmic::{
|
||||||
iced::{
|
iced::{
|
||||||
self,
|
self,
|
||||||
clipboard::dnd::DndAction,
|
clipboard::dnd::DndAction,
|
||||||
|
core::SmolStr,
|
||||||
event,
|
event,
|
||||||
futures::{self, SinkExt},
|
futures::{self, SinkExt},
|
||||||
keyboard::{Event as KeyEvent, Key, Modifiers},
|
keyboard::{Event as KeyEvent, Key, Modifiers},
|
||||||
|
|
@ -64,7 +65,7 @@ use wayland_client::{protocol::wl_output::WlOutput, Proxy};
|
||||||
use crate::operation::{OperationError, OperationErrorType};
|
use crate::operation::{OperationError, OperationErrorType};
|
||||||
use crate::{
|
use crate::{
|
||||||
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
|
clipboard::{ClipboardCopy, ClipboardKind, ClipboardPaste},
|
||||||
config::{AppTheme, Config, DesktopConfig, Favorite, IconSizes, TabConfig},
|
config::{AppTheme, Config, DesktopConfig, Favorite, IconSizes, TabConfig, TypeToSearch},
|
||||||
fl, home_dir,
|
fl, home_dir,
|
||||||
key_bind::key_binds,
|
key_bind::key_binds,
|
||||||
localize::LANGUAGE_SORTER,
|
localize::LANGUAGE_SORTER,
|
||||||
|
|
@ -292,7 +293,7 @@ pub enum Message {
|
||||||
ExtractHere(Option<Entity>),
|
ExtractHere(Option<Entity>),
|
||||||
#[cfg(all(feature = "desktop", feature = "wayland"))]
|
#[cfg(all(feature = "desktop", feature = "wayland"))]
|
||||||
Focused(window::Id),
|
Focused(window::Id),
|
||||||
Key(Modifiers, Key),
|
Key(Modifiers, Key, Option<SmolStr>),
|
||||||
LaunchUrl(String),
|
LaunchUrl(String),
|
||||||
MaybeExit,
|
MaybeExit,
|
||||||
Modifiers(Modifiers),
|
Modifiers(Modifiers),
|
||||||
|
|
@ -338,6 +339,7 @@ pub enum Message {
|
||||||
SearchClear,
|
SearchClear,
|
||||||
SearchInput(String),
|
SearchInput(String),
|
||||||
SetShowDetails(bool),
|
SetShowDetails(bool),
|
||||||
|
SetTypeToSearch(TypeToSearch),
|
||||||
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
SystemThemeModeChange(cosmic_theme::ThemeMode),
|
||||||
Size(Size),
|
Size(Size),
|
||||||
TabActivate(Entity),
|
TabActivate(Entity),
|
||||||
|
|
@ -1524,27 +1526,44 @@ impl App {
|
||||||
|
|
||||||
fn settings(&self) -> Element<Message> {
|
fn settings(&self) -> Element<Message> {
|
||||||
// TODO: Should dialog be updated here too?
|
// TODO: Should dialog be updated here too?
|
||||||
widget::column::with_children(vec![widget::settings::section()
|
widget::settings::view_column(vec![
|
||||||
.title(fl!("appearance"))
|
widget::settings::section()
|
||||||
.add({
|
.title(fl!("appearance"))
|
||||||
let app_theme_selected = match self.config.app_theme {
|
.add({
|
||||||
AppTheme::Dark => 1,
|
let app_theme_selected = match self.config.app_theme {
|
||||||
AppTheme::Light => 2,
|
AppTheme::Dark => 1,
|
||||||
AppTheme::System => 0,
|
AppTheme::Light => 2,
|
||||||
};
|
AppTheme::System => 0,
|
||||||
widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
|
};
|
||||||
&self.app_themes,
|
widget::settings::item::builder(fl!("theme")).control(widget::dropdown(
|
||||||
Some(app_theme_selected),
|
&self.app_themes,
|
||||||
move |index| {
|
Some(app_theme_selected),
|
||||||
Message::AppTheme(match index {
|
move |index| {
|
||||||
1 => AppTheme::Dark,
|
Message::AppTheme(match index {
|
||||||
2 => AppTheme::Light,
|
1 => AppTheme::Dark,
|
||||||
_ => AppTheme::System,
|
2 => AppTheme::Light,
|
||||||
})
|
_ => AppTheme::System,
|
||||||
},
|
})
|
||||||
|
},
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
widget::settings::section()
|
||||||
|
.title(fl!("type-to-search"))
|
||||||
|
.add(widget::radio(
|
||||||
|
widget::text::body(fl!("type-to-search-recursive")),
|
||||||
|
TypeToSearch::Recursive,
|
||||||
|
Some(self.config.type_to_search),
|
||||||
|
Message::SetTypeToSearch,
|
||||||
))
|
))
|
||||||
})
|
.add(widget::radio(
|
||||||
.into()])
|
widget::text::body(fl!("type-to-search-enter-path")),
|
||||||
|
TypeToSearch::EnterPath,
|
||||||
|
Some(self.config.type_to_search),
|
||||||
|
Message::SetTypeToSearch,
|
||||||
|
))
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2187,13 +2206,46 @@ impl Application for App {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::Key(modifiers, key) => {
|
Message::Key(modifiers, key, text) => {
|
||||||
let entity = self.tab_model.active();
|
let entity = self.tab_model.active();
|
||||||
for (key_bind, action) in self.key_binds.iter() {
|
for (key_bind, action) in self.key_binds.iter() {
|
||||||
if key_bind.matches(modifiers, &key) {
|
if key_bind.matches(modifiers, &key) {
|
||||||
return self.update(action.message(Some(entity)));
|
return self.update(action.message(Some(entity)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uncaptured keys with only shift modifiers go to the search or location box
|
||||||
|
if !modifiers.logo()
|
||||||
|
&& !modifiers.control()
|
||||||
|
&& !modifiers.alt()
|
||||||
|
&& matches!(key, Key::Character(_))
|
||||||
|
{
|
||||||
|
if let Some(text) = text {
|
||||||
|
match self.config.type_to_search {
|
||||||
|
TypeToSearch::Recursive => {
|
||||||
|
let mut term = self.search_get().unwrap_or_default().to_string();
|
||||||
|
term.push_str(&text);
|
||||||
|
return self.search_set_active(Some(term));
|
||||||
|
}
|
||||||
|
TypeToSearch::EnterPath => {
|
||||||
|
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||||
|
let location = tab.edit_location.as_ref().map_or_else(
|
||||||
|
|| tab.location.clone(),
|
||||||
|
|x| x.location.clone(),
|
||||||
|
);
|
||||||
|
// Try to add text to end of location
|
||||||
|
if let Some(path) = location.path_opt() {
|
||||||
|
let mut path_string = path.to_string_lossy().to_string();
|
||||||
|
path_string.push_str(&text);
|
||||||
|
tab.edit_location = Some(
|
||||||
|
location.with_path(PathBuf::from(path_string)).into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Message::MaybeExit => {
|
Message::MaybeExit => {
|
||||||
if self.window_id_opt.is_none() && self.pending_operations.is_empty() {
|
if self.window_id_opt.is_none() && self.pending_operations.is_empty() {
|
||||||
|
|
@ -2860,6 +2912,10 @@ impl Application for App {
|
||||||
config_set!(show_details, show_details);
|
config_set!(show_details, show_details);
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
|
Message::SetTypeToSearch(type_to_search) => {
|
||||||
|
config_set!(type_to_search, type_to_search);
|
||||||
|
return self.update_config();
|
||||||
|
}
|
||||||
Message::SystemThemeModeChange(_theme_mode) => {
|
Message::SystemThemeModeChange(_theme_mode) => {
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
|
|
@ -4577,8 +4633,13 @@ impl Application for App {
|
||||||
|
|
||||||
let mut subscriptions = vec![
|
let mut subscriptions = vec![
|
||||||
event::listen_with(|event, status, window_id| match event {
|
event::listen_with(|event, status, window_id| match event {
|
||||||
Event::Keyboard(KeyEvent::KeyPressed { key, modifiers, .. }) => match status {
|
Event::Keyboard(KeyEvent::KeyPressed {
|
||||||
event::Status::Ignored => Some(Message::Key(modifiers, key)),
|
key,
|
||||||
|
modifiers,
|
||||||
|
text,
|
||||||
|
..
|
||||||
|
}) => match status {
|
||||||
|
event::Status::Ignored => Some(Message::Key(modifiers, key, text)),
|
||||||
event::Status::Captured => None,
|
event::Status::Captured => None,
|
||||||
},
|
},
|
||||||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,12 @@ impl Favorite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
|
pub enum TypeToSearch {
|
||||||
|
Recursive,
|
||||||
|
EnterPath,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
|
@ -103,6 +109,7 @@ pub struct Config {
|
||||||
pub favorites: Vec<Favorite>,
|
pub favorites: Vec<Favorite>,
|
||||||
pub show_details: bool,
|
pub show_details: bool,
|
||||||
pub tab: TabConfig,
|
pub tab: TabConfig,
|
||||||
|
pub type_to_search: TypeToSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
@ -150,6 +157,7 @@ impl Default for Config {
|
||||||
],
|
],
|
||||||
show_details: false,
|
show_details: false,
|
||||||
tab: TabConfig::default(),
|
tab: TabConfig::default(),
|
||||||
|
type_to_search: TypeToSearch::Recursive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
src/tab.rs
14
src/tab.rs
|
|
@ -1055,6 +1055,16 @@ impl std::fmt::Display for Location {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Location {
|
impl Location {
|
||||||
|
pub fn normalize(&self) -> Self {
|
||||||
|
if let Some(mut path) = self.path_opt().map(|x| x.to_path_buf()) {
|
||||||
|
// Add trailing slash if location is a path
|
||||||
|
path.push("");
|
||||||
|
self.with_path(path)
|
||||||
|
} else {
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn path_opt(&self) -> Option<&PathBuf> {
|
pub fn path_opt(&self) -> Option<&PathBuf> {
|
||||||
match self {
|
match self {
|
||||||
Self::Desktop(path, ..) => Some(path),
|
Self::Desktop(path, ..) => Some(path),
|
||||||
|
|
@ -1873,7 +1883,7 @@ impl Tab {
|
||||||
pub fn new(location: Location, config: TabConfig) -> Self {
|
pub fn new(location: Location, config: TabConfig) -> Self {
|
||||||
let history = vec![location.clone()];
|
let history = vec![location.clone()];
|
||||||
Self {
|
Self {
|
||||||
location,
|
location: location.normalize(),
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
location_context_menu_point: None,
|
location_context_menu_point: None,
|
||||||
location_context_menu_index: None,
|
location_context_menu_index: None,
|
||||||
|
|
@ -2198,7 +2208,7 @@ impl Tab {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_location(&mut self, location: &Location, history_i_opt: Option<usize>) {
|
pub fn change_location(&mut self, location: &Location, history_i_opt: Option<usize>) {
|
||||||
self.location = location.clone();
|
self.location = location.normalize();
|
||||||
self.context_menu = None;
|
self.context_menu = None;
|
||||||
self.edit_location = None;
|
self.edit_location = None;
|
||||||
self.items_opt = None;
|
self.items_opt = None;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue