Merge pull request #1471 from wowitsjack/type-to-select
add type-to-select keyboard option
This commit is contained in:
commit
03fab5b7f5
5 changed files with 85 additions and 1 deletions
|
|
@ -297,6 +297,7 @@ light = Light
|
|||
type-to-search = Type to search
|
||||
type-to-search-recursive = Searches the current folder and all subfolders
|
||||
type-to-search-enter-path = Enters the path to the directory or file
|
||||
type-to-search-select = Selects the first matching file or folder
|
||||
|
||||
# Context menu
|
||||
add-to-sidebar = Add to sidebar
|
||||
|
|
|
|||
32
src/app.rs
32
src/app.rs
|
|
@ -733,6 +733,8 @@ pub struct App {
|
|||
windows: FxHashMap<window::Id, Window>,
|
||||
nav_dnd_hover: Option<(Location, Instant)>,
|
||||
tab_dnd_hover: Option<(Entity, Instant)>,
|
||||
type_select_prefix: String,
|
||||
type_select_last_key: Option<Instant>,
|
||||
nav_drag_id: DragId,
|
||||
tab_drag_id: DragId,
|
||||
auto_scroll_speed: Option<i16>,
|
||||
|
|
@ -1985,6 +1987,12 @@ impl App {
|
|||
Some(self.config.type_to_search),
|
||||
Message::SetTypeToSearch,
|
||||
))
|
||||
.add(widget::radio(
|
||||
widget::text::body(fl!("type-to-search-select")),
|
||||
TypeToSearch::SelectByPrefix,
|
||||
Some(self.config.type_to_search),
|
||||
Message::SetTypeToSearch,
|
||||
))
|
||||
.into(),
|
||||
widget::settings::section()
|
||||
.title(fl!("other"))
|
||||
|
|
@ -2213,6 +2221,8 @@ impl Application for App {
|
|||
windows: FxHashMap::default(),
|
||||
nav_dnd_hover: None,
|
||||
tab_dnd_hover: None,
|
||||
type_select_prefix: String::new(),
|
||||
type_select_last_key: None,
|
||||
nav_drag_id: DragId::new(),
|
||||
tab_drag_id: DragId::new(),
|
||||
auto_scroll_speed: None,
|
||||
|
|
@ -3039,6 +3049,28 @@ impl Application for App {
|
|||
}
|
||||
}
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key {
|
||||
if last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT {
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
|
||||
if let Some(tab) = self.tab_model.data_mut::<Tab>(entity) {
|
||||
tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ impl Favorite {
|
|||
pub enum TypeToSearch {
|
||||
Recursive,
|
||||
EnterPath,
|
||||
SelectByPrefix,
|
||||
}
|
||||
|
||||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use cosmic::{
|
|||
futures::{self, SinkExt},
|
||||
keyboard::{Event as KeyEvent, Key, Modifiers, key::Named},
|
||||
stream, window,
|
||||
widget::scrollable,
|
||||
},
|
||||
iced_core::widget::operation,
|
||||
iced_winit::{self, SurfaceIdWrapper},
|
||||
|
|
@ -568,6 +569,8 @@ struct App {
|
|||
FxHashSet<PathBuf>,
|
||||
)>,
|
||||
auto_scroll_speed: Option<i16>,
|
||||
type_select_prefix: String,
|
||||
type_select_last_key: Option<Instant>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
|
|
@ -1038,6 +1041,8 @@ impl Application for App {
|
|||
key_binds,
|
||||
watcher_opt: None,
|
||||
auto_scroll_speed: None,
|
||||
type_select_prefix: String::new(),
|
||||
type_select_last_key: None,
|
||||
};
|
||||
|
||||
let commands = Task::batch([
|
||||
|
|
@ -1448,6 +1453,26 @@ impl Application for App {
|
|||
Some(location.with_path(PathBuf::from(path_string)).into());
|
||||
}
|
||||
}
|
||||
TypeToSearch::SelectByPrefix => {
|
||||
// Reset buffer if timeout elapsed
|
||||
if let Some(last_key) = self.type_select_last_key {
|
||||
if last_key.elapsed() >= tab::TYPE_SELECT_TIMEOUT {
|
||||
self.type_select_prefix.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Accumulate character and select
|
||||
self.type_select_prefix.push_str(&text.to_lowercase());
|
||||
self.type_select_last_key = Some(Instant::now());
|
||||
|
||||
self.tab.select_by_prefix(&self.type_select_prefix);
|
||||
if let Some(offset) = self.tab.select_focus_scroll() {
|
||||
return scrollable::scroll_to(
|
||||
self.tab.scrollable_id.clone(),
|
||||
offset,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
src/tab.rs
27
src/tab.rs
|
|
@ -98,6 +98,7 @@ use uzers::{get_group_by_gid, get_user_by_uid};
|
|||
|
||||
pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500);
|
||||
pub const HOVER_DURATION: Duration = Duration::from_millis(1600);
|
||||
pub const TYPE_SELECT_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
//TODO: best limit for search items
|
||||
const MAX_SEARCH_LATENCY: Duration = Duration::from_millis(20);
|
||||
const MAX_SEARCH_RESULTS: usize = 200;
|
||||
|
|
@ -2821,6 +2822,30 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
|
||||
/// Selects the first item whose name starts with the given prefix (case-insensitive).
|
||||
/// Returns true if an item was selected.
|
||||
pub fn select_by_prefix(&mut self, prefix: &str) -> bool {
|
||||
let prefix_lower = prefix.to_lowercase();
|
||||
self.select_focus = None;
|
||||
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
// First, deselect all items
|
||||
for item in items.iter_mut() {
|
||||
item.selected = false;
|
||||
}
|
||||
|
||||
// Find first matching item
|
||||
for (i, item) in items.iter_mut().enumerate() {
|
||||
if item.name.to_lowercase().starts_with(&prefix_lower) {
|
||||
item.selected = true;
|
||||
self.select_focus = Some(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn select_paths(&mut self, paths: Vec<PathBuf>) {
|
||||
self.select_focus = None;
|
||||
if let Some(ref mut items) = self.items_opt {
|
||||
|
|
@ -2917,7 +2942,7 @@ impl Tab {
|
|||
item.pos_opt.get()
|
||||
}
|
||||
|
||||
fn select_focus_scroll(&mut self) -> Option<AbsoluteOffset> {
|
||||
pub(crate) fn select_focus_scroll(&mut self) -> Option<AbsoluteOffset> {
|
||||
let items = self.items_opt.as_ref()?;
|
||||
let item = items.get(self.select_focus?)?;
|
||||
let rect = item.rect_opt.get()?;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue