/
githubmirror
/
lapce
Обзор
Документация
Войти
/
githubmirror
/
lapce
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
lapce-app/src/keymap.rs
562 строки
21 KB
Hanif Ariffin
Fix compilation with Rust 2024 Edition (#3637)
19 июн 2025, 10:52
Не верифицирован
19 июн 2025, 10:52
8d50fda
Код
Авторство
О чём код?
use std::{rc::Rc, sync::Arc}; use floem::{ View, event::{Event, EventListener}, reactive::{ Memo, ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, create_effect, create_memo, create_rw_signal, }, style::CursorStyle, views::{ Decorators, container, dyn_stack, label, scroll, stack, text, virtual_stack, }, }; use lapce_core::mode::Modes; use crate::{ command::LapceCommand, config::{LapceConfig, color::LapceColor}, keypress::{ KeyPressData, keymap::{KeyMap, KeyMapPress}, }, main_split::Editors, text_input::TextInputBuilder, window_tab::CommonData, }; #[derive(Clone)] pub struct KeymapPicker { cmd: RwSignal<Option<LapceCommand>>, keymap: RwSignal<Option<KeyMap>>, keys: RwSignal<Vec<(KeyMapPress, bool)>>, } pub fn keymap_view(editors: Editors, common: Rc<CommonData>) -> impl View { let config = common.config; let keypress = common.keypress; let ui_line_height_memo = common.ui_line_height; let ui_line_height = move || ui_line_height_memo.get() * 1.2; let modal = create_memo(move |_| config.get().core.modal); let picker = KeymapPicker { cmd: create_rw_signal(None), keymap: create_rw_signal(None), keys: create_rw_signal(Vec::new()), }; let cx = Scope::current(); let text_input_view = TextInputBuilder::new().build(cx, editors, common.clone()); let doc = text_input_view.doc_signal(); let items = move || { let doc = doc.get(); let pattern = doc.buffer.with(|b| b.to_string().to_lowercase()); let keypress = keypress.get(); let mut items = keypress .commands_with_keymap .iter() .filter_map(|keymap| { let cmd = keypress.commands.get(&keymap.command).cloned()?; let cmd_name_contains_pattern = cmd.kind.str().replace('_', " ").contains(&pattern); let cmd_desc_contains_pattern = cmd .kind .desc() .map(|desc| desc.to_lowercase().contains(&pattern)) .unwrap_or(false); let shortcut_contains_pattern = keymap .key .iter() .any(|k| k.label().trim().to_lowercase().contains(&pattern)); let when_contains_pattern = keymap .when .as_ref() .map(|when| when.to_lowercase().contains(&pattern)) .unwrap_or(false); if cmd_name_contains_pattern || cmd_desc_contains_pattern || shortcut_contains_pattern || when_contains_pattern { Some((cmd, Some(keymap.clone()))) } else { None } }) .collect::<im::Vector<(LapceCommand, Option<KeyMap>)>>(); items.extend(keypress.commands_without_keymap.iter().filter_map(|cmd| { let match_pattern = cmd.kind.str().replace('_', " ").contains(&pattern) || cmd .kind .desc() .map(|desc| desc.to_lowercase().contains(&pattern)) .unwrap_or(false); if !match_pattern { return None; } Some((cmd.clone(), None)) })); items .into_iter() .enumerate() .collect::<im::Vector<(usize, (LapceCommand, Option<KeyMap>))>>() }; let view_fn = move |(i, (cmd, keymap)): (usize, (LapceCommand, Option<KeyMap>))| { let local_keymap = keymap.clone(); let local_cmd = cmd.clone(); stack(( container( text( cmd.kind .desc() .map(|desc| desc.to_string()) .unwrap_or_else(|| cmd.kind.str().replace('_', " ")), ) .style(|s| { s.text_ellipsis() .absolute() .items_center() .min_width(0.0) .padding_horiz(10.0) .size_pct(100.0, 100.0) }), ) .style(move |s| { s.height_pct(100.0) .min_width(0.0) .flex_basis(0.0) .flex_grow(1.0) .border_right(1.0) .border_color(config.get().color(LapceColor::LAPCE_BORDER)) }), { let keymap = keymap.clone(); dyn_stack( move || { keymap .as_ref() .map(|keymap| { keymap .key .iter() .map(|key| key.label()) .filter(|l| !l.is_empty()) .collect::<Vec<String>>() }) .unwrap_or_default() }, |k| k.clone(), move |key| { text(key.clone()).style(move |s| { s.padding_horiz(5.0) .padding_vert(1.0) .margin_right(5.0) .border(1.0) .border_radius(3.0) .border_color( config.get().color(LapceColor::LAPCE_BORDER), ) }) }, ) .style(move |s| { s.items_center() .padding_horiz(10.0) .min_width(200.0) .height_pct(100.0) .border_right(1.0) .border_color( config.get().color(LapceColor::LAPCE_BORDER), ) }) }, { let keymap = keymap.clone(); let bits = [ (Modes::INSERT, "Insert"), (Modes::NORMAL, "Normal"), (Modes::VISUAL, "Visual"), (Modes::TERMINAL, "Terminal"), ]; let modes = keymap .as_ref() .map(|keymap| { bits.iter() .filter_map(|(bit, mode)| { if keymap.modes.contains(*bit) { Some(mode.to_string()) } else { None } }) .collect::<Vec<String>>() }) .unwrap_or_default(); dyn_stack( move || modes.clone(), |m| m.clone(), move |mode| { text(mode.clone()).style(move |s| { s.padding_horiz(5.0) .padding_vert(1.0) .margin_right(5.0) .border(1.0) .border_radius(3.0) .border_color( config.get().color(LapceColor::LAPCE_BORDER), ) }) }, ) .style(move |s| { s.items_center() .padding_horiz(10.0) .min_width(200.0) .height_pct(100.0) .border_right(1.0) .border_color( config.get().color(LapceColor::LAPCE_BORDER), ) .apply_if(!modal.get(), |s| s.hide()) }) }, container( text( keymap .as_ref() .and_then(|keymap| keymap.when.clone()) .unwrap_or_default(), ) .style(|s| { s.text_ellipsis() .absolute() .items_center() .min_width(0.0) .padding_horiz(10.0) .size_pct(100.0, 100.0) }), ) .style(move |s| { s.height_pct(100.0) .min_width(0.0) .flex_basis(0.0) .flex_grow(1.0) }), )) .on_click_stop(move |_| { let keymap = if let Some(keymap) = local_keymap.clone() { keymap } else { KeyMap { command: local_cmd.kind.str().to_string(), key: Vec::new(), modes: Modes::empty(), when: None, } }; picker.keymap.set(Some(keymap)); picker.cmd.set(Some(local_cmd.clone())); picker.keys.update(|keys| { keys.clear(); }); }) .style(move |s| { let config = config.get(); s.items_center() .height(ui_line_height() as f32) .width_pct(100.0) .apply_if(i % 2 > 0, |s| { s.background(config.color(LapceColor::EDITOR_CURRENT_LINE)) }) .border_bottom(1.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) }) }; stack(( container( text_input_view .placeholder(|| "Search Key Bindings".to_string()) .keyboard_navigable() .request_focus(|| {}) .style(move |s| { s.width_pct(100.0) .border_radius(6.0) .border(1.0) .border_color(config.get().color(LapceColor::LAPCE_BORDER)) }), ) .style(|s| s.padding_bottom(10.0).width_pct(100.0)), stack(( container(text("Command").style(move |s| { s.text_ellipsis().padding_horiz(10.0).min_width(0.0) })) .style(move |s| { s.items_center() .height_pct(100.0) .min_width(0.0) .flex_basis(0.0) .flex_grow(1.0) .border_right(1.0) .border_color(config.get().color(LapceColor::LAPCE_BORDER)) }), text("Key Binding").style(move |s| { s.width(200.0) .items_center() .padding_horiz(10.0) .height_pct(100.0) .border_right(1.0) .border_color(config.get().color(LapceColor::LAPCE_BORDER)) }), text("Modes").style(move |s| { s.width(200.0) .items_center() .padding_horiz(10.0) .height_pct(100.0) .border_right(1.0) .border_color(config.get().color(LapceColor::LAPCE_BORDER)) .apply_if(!modal.get(), |s| s.hide()) }), container(text("When").style(move |s| { s.text_ellipsis().padding_horiz(10.0).min_width(0.0) })) .style(move |s| { s.items_center() .height_pct(100.0) .min_width(0.0) .flex_basis(0.0) .flex_grow(1.0) }), )) .style(move |s| { let config = config.get(); s.font_bold() .height(ui_line_height() as f32) .width_pct(100.0) .border_top(1.0) .border_bottom(1.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) .background(config.color(LapceColor::EDITOR_CURRENT_LINE)) }), container( scroll( virtual_stack( items, |(i, (cmd, keymap)): &( usize, (LapceCommand, Option<KeyMap>), )| { (*i, cmd.kind.str(), keymap.clone()) }, view_fn, ) .item_size_fixed(ui_line_height) .style(|s| s.flex_col().width_pct(100.0)), ) .style(|s| s.absolute().size_pct(100.0, 100.0)), ) .style(|s| s.width_pct(100.0).flex_basis(0.0).flex_grow(1.0)), keyboard_picker_view(picker, common.ui_line_height, config), )) .style(|s| { s.absolute() .size_pct(100.0, 100.0) .flex_col() .padding_top(20.0) .padding_left(20.0) .padding_right(20.0) }) } fn keyboard_picker_view( picker: KeymapPicker, ui_line_height: Memo<f64>, config: ReadSignal<Arc<LapceConfig>>, ) -> impl View { let picker_cmd = picker.cmd; let view = container( stack(( label(move || { picker_cmd.with(|cmd| { cmd.as_ref() .map(|cmd| { cmd.kind .desc() .map(|desc| desc.to_string()) .unwrap_or_else(|| cmd.kind.str().replace('_', " ")) }) .unwrap_or_default() }) }), dyn_stack( move || { picker .keys .get() .iter() .map(|(key, _)| key.label()) .filter(|l| !l.is_empty()) .enumerate() .collect::<Vec<(usize, String)>>() }, |(i, k)| (*i, k.clone()), move |(_, key)| { text(key.clone()).style(move |s| { s.padding_horiz(5.0) .padding_vert(1.0) .margin_right(5.0) .height(ui_line_height.get() as f32) .border(1.0) .border_radius(6.0) .border_color( config.get().color(LapceColor::LAPCE_BORDER), ) }) }, ) .style(move |s| { let config = config.get(); s.items_center() .justify_center() .width_pct(100.0) .margin_top(20.0) .height((ui_line_height.get() as f32) * 1.2) .border(1.0) .border_radius(6.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) .background(config.color(LapceColor::EDITOR_BACKGROUND)) }), stack(( text("Save") .style(move |s| { let config = config.get(); s.width(100.0) .justify_center() .padding_vert(8.0) .border(1.0) .border_radius(6.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) .hover(|s| { s.cursor(CursorStyle::Pointer).background( config .color(LapceColor::PANEL_HOVERED_BACKGROUND), ) }) .active(|s| { s.background(config.color( LapceColor::PANEL_HOVERED_ACTIVE_BACKGROUND, )) }) }) .on_click_stop(move |_| { let keymap = picker.keymap.get_untracked(); if let Some(keymap) = keymap { let keys = picker.keys.get_untracked(); picker.keymap.set(None); KeyPressData::update_file( &keymap, &keys .iter() .map(|(key, _)| key.clone()) .collect::<Vec<KeyMapPress>>(), ); } }), text("Cancel") .style(move |s| { let config = config.get(); s.margin_left(20.0) .width(100.0) .justify_center() .padding_vert(8.0) .border(1.0) .border_radius(6.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) .hover(|s| { s.cursor(CursorStyle::Pointer).background( config .color(LapceColor::PANEL_HOVERED_BACKGROUND), ) }) .active(|s| { s.background(config.color( LapceColor::PANEL_HOVERED_ACTIVE_BACKGROUND, )) }) }) .on_click_stop(move |_| { picker.keymap.set(None); }), )) .style(move |s| { let config = config.get(); s.items_center() .justify_center() .width_pct(100.0) .margin_top(20.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) }), )) .on_event_stop(EventListener::PointerDown, |_| {}) .style(move |s| { let config = config.get(); s.items_center() .flex_col() .padding(20.0) .width(400.0) .border(1.0) .border_radius(6.0) .border_color(config.color(LapceColor::LAPCE_BORDER)) .background(config.color(LapceColor::PANEL_BACKGROUND)) }), ) .keyboard_navigable() .on_event_stop(EventListener::KeyDown, move |event| { if let Event::KeyDown(key_event) = event { if let Some(keypress) = KeyPressData::keypress(key_event) { if let Some(keypress) = keypress.keymap_press() { picker.keys.update(|keys| { if let Some((last_key, last_key_confirmed)) = keys.last() { if !*last_key_confirmed && last_key.is_modifiers() { keys.pop(); } } if keys.len() == 2 { keys.clear(); } keys.push((keypress, false)); }) } } } }) .on_event_stop(EventListener::KeyUp, move |event| { if let Event::KeyUp(_key_event) = event { picker.keys.update(|keys| { if let Some((_last_key, last_key_confirmed)) = keys.last_mut() { *last_key_confirmed = true; } }) } }) .style(move |s| { s.absolute() .size_pct(100.0, 100.0) .items_center() .justify_center() .apply_if(picker.keymap.with(|keymap| keymap.is_none()), |s| s.hide()) }) .debug_name("keyboard picker"); let id = view.id(); create_effect(move |_| { if picker.keymap.with(|k| k.is_some()) { id.request_focus(); } }); view }