Implement find and improvements for focus
This commit is contained in:
parent
81a0bbc094
commit
f3a8fef8f8
10 changed files with 358 additions and 105 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -998,7 +998,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-config"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"atomicwrites",
|
||||
"cosmic-config-derive",
|
||||
|
|
@ -1013,7 +1013,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-config-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
|
|
@ -1083,7 +1083,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-theme"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"almost",
|
||||
"cosmic-config",
|
||||
|
|
@ -2612,7 +2612,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_accessibility",
|
||||
"iced_core",
|
||||
|
|
@ -2627,7 +2627,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_accessibility"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_winit",
|
||||
|
|
@ -2636,7 +2636,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"instant",
|
||||
|
|
@ -2652,7 +2652,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_futures"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"iced_core",
|
||||
|
|
@ -2665,7 +2665,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_graphics"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
|
|
@ -2688,7 +2688,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_renderer"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_tiny_skia",
|
||||
|
|
@ -2701,7 +2701,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_runtime"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
|
|
@ -2711,7 +2711,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_style"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_core",
|
||||
"once_cell",
|
||||
|
|
@ -2721,7 +2721,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_tiny_skia"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cosmic-text",
|
||||
|
|
@ -2739,7 +2739,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_wgpu"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bytemuck",
|
||||
|
|
@ -2759,7 +2759,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_widget"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_renderer",
|
||||
"iced_runtime",
|
||||
|
|
@ -2773,7 +2773,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_winit"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_runtime",
|
||||
|
|
@ -3092,7 +3092,7 @@ checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
|||
[[package]]
|
||||
name = "libcosmic"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic#5b2ac941c36e1b7e2b4fa9a9162b64dc10afffef"
|
||||
source = "git+https://github.com/pop-os/libcosmic#68c760e65203c5124844c2083224d8c83010ed1e"
|
||||
dependencies = [
|
||||
"apply",
|
||||
"ashpd",
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ default-font-size = Default font size
|
|||
keyboard-shortcuts = Keyboard shortcuts
|
||||
enable-vim-bindings = Enable Vim bindings
|
||||
|
||||
# Find
|
||||
find-placeholder = Find...
|
||||
|
||||
# Menu
|
||||
|
||||
## File
|
||||
|
|
|
|||
3
res/icons/edit-clear-symbolic.svg
Normal file
3
res/icons/edit-clear-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 2.00305L0 8.00305L5 14.0031H16V2.00305H5ZM7 5.00305H8C8.277 5.00305 8.526 5.11505 8.707 5.29605L10 6.58905L11.293 5.29605C11.3856 5.20288 11.4958 5.129 11.6171 5.0787C11.7385 5.02841 11.8686 5.0027 12 5.00305H13V6.00305C13.0002 6.1344 12.9744 6.26448 12.9241 6.38582C12.8738 6.50715 12.8 6.61735 12.707 6.71005L11.414 8.00305L12.707 9.29605C12.887 9.47605 13 9.72605 13 10.0031V11.0031H12C11.8687 11.0032 11.7386 10.9775 11.6172 10.9272C11.4959 10.8769 11.3857 10.8031 11.293 10.7101L10 9.41705L8.707 10.7101C8.6143 10.8031 8.50409 10.8769 8.38275 10.9272C8.26141 10.9775 8.13134 11.0032 8 11.0031H7V10.0031C7 9.72605 7.112 9.47605 7.293 9.29605L8.586 8.00305L7.293 6.71005C7.19996 6.61735 7.12618 6.50715 7.0759 6.38582C7.02561 6.26448 6.99981 6.1344 7 6.00305V5.00305Z" fill="#232323"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 904 B |
3
res/icons/go-up-symbolic.svg
Normal file
3
res/icons/go-up-symbolic.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.44039 10.063L7.50339 4L13.5654 10.063C15.0034 11.5 13.5034 12.75 12.4404 11.688L7.50339 6.75L2.56539 11.688C1.75339 12.5 0.253389 11.25 1.44039 10.063Z" fill="#232323"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 285 B |
|
|
@ -19,6 +19,8 @@ pub enum Action {
|
|||
CloseProject,
|
||||
Copy,
|
||||
Cut,
|
||||
Find,
|
||||
FindAndReplace,
|
||||
NewFile,
|
||||
NewWindow,
|
||||
OpenFileDialog,
|
||||
|
|
@ -42,6 +44,8 @@ impl Action {
|
|||
Self::CloseProject => Message::CloseProject,
|
||||
Self::Copy => Message::Copy,
|
||||
Self::Cut => Message::Cut,
|
||||
Self::Find => Message::Find(Some(false)),
|
||||
Self::FindAndReplace => Message::Find(Some(true)),
|
||||
Self::NewFile => Message::NewFile,
|
||||
Self::NewWindow => Message::NewWindow,
|
||||
Self::OpenFileDialog => Message::OpenFileDialog,
|
||||
|
|
@ -111,6 +115,8 @@ impl KeyBind {
|
|||
bind!([Ctrl], W, CloseFile);
|
||||
bind!([Ctrl], X, Cut);
|
||||
bind!([Ctrl], C, Copy);
|
||||
bind!([Ctrl], F, Find);
|
||||
bind!([Ctrl], H, FindAndReplace);
|
||||
bind!([Ctrl], V, Paste);
|
||||
bind!([Ctrl], T, NewFile);
|
||||
bind!([Ctrl], N, NewWindow);
|
||||
|
|
|
|||
|
|
@ -30,9 +30,11 @@ impl IconCache {
|
|||
};
|
||||
}
|
||||
|
||||
bundle!("edit-clear-symbolic", 16);
|
||||
bundle!("folder-open-symbolic", 16);
|
||||
bundle!("go-down-symbolic", 16);
|
||||
bundle!("go-next-symbolic", 16);
|
||||
bundle!("go-up-symbolic", 16);
|
||||
bundle!("list-add-symbolic", 16);
|
||||
bundle!("object-select-symbolic", 16);
|
||||
bundle!("window-close-symbolic", 16);
|
||||
|
|
|
|||
161
src/main.rs
161
src/main.rs
|
|
@ -181,6 +181,11 @@ pub enum Message {
|
|||
Cut,
|
||||
DefaultFont(usize),
|
||||
DefaultFontSize(usize),
|
||||
Find(Option<bool>),
|
||||
FindNext,
|
||||
FindPrevious,
|
||||
FindReplaceValueChanged(String),
|
||||
FindSearchValueChanged(String),
|
||||
GitProjectStatus(Vec<(String, PathBuf, Vec<GitStatus>)>),
|
||||
Key(keyboard::Modifiers, keyboard::KeyCode),
|
||||
NewFile,
|
||||
|
|
@ -241,6 +246,13 @@ impl ContextPage {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Find {
|
||||
None,
|
||||
Find,
|
||||
FindAndReplace,
|
||||
}
|
||||
|
||||
pub struct App {
|
||||
core: Core,
|
||||
nav_model: segmented_button::SingleSelectModel,
|
||||
|
|
@ -253,6 +265,11 @@ pub struct App {
|
|||
font_sizes: Vec<u16>,
|
||||
theme_names: Vec<String>,
|
||||
context_page: ContextPage,
|
||||
text_box_id: widget::Id,
|
||||
find_opt: Option<bool>,
|
||||
find_replace_value: String,
|
||||
find_search_id: widget::Id,
|
||||
find_search_value: String,
|
||||
git_project_status: Option<Vec<(String, PathBuf, Vec<GitStatus>)>>,
|
||||
projects: Vec<(String, PathBuf)>,
|
||||
project_search_id: widget::Id,
|
||||
|
|
@ -440,6 +457,21 @@ impl App {
|
|||
self.update_config()
|
||||
}
|
||||
|
||||
fn update_focus(&self) -> Command<Message> {
|
||||
if self.core.window.show_context {
|
||||
match self.context_page {
|
||||
ContextPage::ProjectSearch => {
|
||||
widget::text_input::focus(self.project_search_id.clone())
|
||||
}
|
||||
_ => Command::none(),
|
||||
}
|
||||
} else if self.find_opt.is_some() {
|
||||
widget::text_input::focus(self.find_search_id.clone())
|
||||
} else {
|
||||
widget::text_input::focus(self.text_box_id.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn update_nav_bar_active(&mut self) {
|
||||
let tab_path_opt = match self.active_tab() {
|
||||
Some(Tab::Editor(tab)) => tab.path_opt.clone(),
|
||||
|
|
@ -504,7 +536,7 @@ impl App {
|
|||
|
||||
let window_title = format!("{title} - COSMIC Text Editor");
|
||||
self.set_header_title(title.clone());
|
||||
self.set_window_title(window_title)
|
||||
Command::batch([self.set_window_title(window_title), self.update_focus()])
|
||||
}
|
||||
|
||||
fn document_statistics(&self) -> Element<Message> {
|
||||
|
|
@ -942,6 +974,11 @@ impl Application for App {
|
|||
font_sizes,
|
||||
theme_names,
|
||||
context_page: ContextPage::Settings,
|
||||
text_box_id: widget::Id::unique(),
|
||||
find_opt: None,
|
||||
find_replace_value: String::new(),
|
||||
find_search_id: widget::Id::unique(),
|
||||
find_search_value: String::new(),
|
||||
git_project_status: None,
|
||||
projects: Vec::new(),
|
||||
project_search_id: widget::Id::unique(),
|
||||
|
|
@ -1021,6 +1058,25 @@ impl Application for App {
|
|||
Some(&self.nav_model)
|
||||
}
|
||||
|
||||
fn on_context_drawer(&mut self) -> Command<Message> {
|
||||
// Focus correct widget
|
||||
self.update_focus()
|
||||
}
|
||||
|
||||
//TODO: currently the first escape unfocuses, and the second calls this function
|
||||
fn on_escape(&mut self) -> Command<Message> {
|
||||
if self.core.window.show_context {
|
||||
// Close context drawer if open
|
||||
self.core.window.show_context = false;
|
||||
} else if self.find_opt.is_some() {
|
||||
// Close find if open
|
||||
self.find_opt = None;
|
||||
}
|
||||
|
||||
// Focus correct widget
|
||||
self.update_focus()
|
||||
}
|
||||
|
||||
fn on_nav_select(&mut self, id: nav_bar::Id) -> Command<Message> {
|
||||
// Toggle open state and get clone of node data
|
||||
let node_opt = match self.nav_model.data_mut::<ProjectNode>(id) {
|
||||
|
|
@ -1160,6 +1216,38 @@ impl Application for App {
|
|||
log::warn!("failed to find font with index {}", index);
|
||||
}
|
||||
},
|
||||
Message::Find(find_opt) => {
|
||||
self.find_opt = find_opt;
|
||||
|
||||
// Focus correct input
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::FindNext => {
|
||||
if !self.find_search_value.is_empty() {
|
||||
if let Some(Tab::Editor(tab)) = self.active_tab() {
|
||||
tab.search(&self.find_search_value, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Focus correct input
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::FindPrevious => {
|
||||
if !self.find_search_value.is_empty() {
|
||||
if let Some(Tab::Editor(tab)) = self.active_tab() {
|
||||
tab.search(&self.find_search_value, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Focus correct input
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::FindReplaceValueChanged(value) => {
|
||||
self.find_replace_value = value;
|
||||
}
|
||||
Message::FindSearchValueChanged(value) => {
|
||||
self.find_search_value = value;
|
||||
}
|
||||
Message::GitProjectStatus(project_status) => {
|
||||
self.git_project_status = Some(project_status);
|
||||
}
|
||||
|
|
@ -1384,8 +1472,8 @@ impl Application for App {
|
|||
Message::ProjectSearchResult(project_search_result) => {
|
||||
self.project_search_result = Some(project_search_result);
|
||||
|
||||
// Ensure input remains focused
|
||||
return widget::text_input::focus(self.project_search_id.clone());
|
||||
// Focus correct input
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::ProjectSearchSubmit => {
|
||||
//TODO: Figure out length requirements?
|
||||
|
|
@ -1595,13 +1683,12 @@ impl Application for App {
|
|||
|x| x,
|
||||
);
|
||||
}
|
||||
ContextPage::ProjectSearch => {
|
||||
// Ensure focus of correct input
|
||||
return widget::text_input::focus(self.project_search_id.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure focus of correct input
|
||||
return self.update_focus();
|
||||
}
|
||||
Message::ToggleLineNumbers => {
|
||||
self.config.line_numbers = !self.config.line_numbers;
|
||||
|
|
@ -1713,23 +1800,22 @@ impl Application for App {
|
|||
}
|
||||
};
|
||||
let mut text_box = text_box(&tab.editor, self.config.metrics())
|
||||
.id(self.text_box_id.clone())
|
||||
.on_changed(Message::TabChanged(tab_id))
|
||||
.has_context_menu(tab.context_menu.is_some())
|
||||
.on_context_menu(move |position_opt| {
|
||||
Message::TabContextMenu(tab_id, position_opt)
|
||||
});
|
||||
if self.config.line_numbers {
|
||||
text_box = text_box.line_numbers();
|
||||
}
|
||||
let tab_element: Element<'_, Message> = match tab.context_menu {
|
||||
Some(position) => widget::popover(
|
||||
text_box.context_menu(position),
|
||||
menu::context_menu(&self.config, tab_id),
|
||||
)
|
||||
.position(position)
|
||||
.into(),
|
||||
None => text_box.into(),
|
||||
let mut popover =
|
||||
widget::popover(text_box, menu::context_menu(&self.config, tab_id));
|
||||
popover = match tab.context_menu {
|
||||
Some(position) => popover.position(position),
|
||||
None => popover.show_popup(false),
|
||||
};
|
||||
tab_column = tab_column.push(tab_element);
|
||||
tab_column = tab_column.push(popover);
|
||||
tab_column = tab_column.push(text(status).font(Font::MONOSPACE));
|
||||
}
|
||||
Some(Tab::GitDiff(tab)) => {
|
||||
|
|
@ -1789,6 +1875,49 @@ impl Application for App {
|
|||
None => {}
|
||||
}
|
||||
|
||||
if let Some(replace) = &self.find_opt {
|
||||
let text_input =
|
||||
widget::text_input::text_input(fl!("find-placeholder"), &self.find_search_value)
|
||||
.id(self.find_search_id.clone())
|
||||
.on_input(Message::FindSearchValueChanged)
|
||||
//TODO: shift+enter for FindPrevious
|
||||
.on_submit(Message::FindNext)
|
||||
.width(Length::Fixed(320.0))
|
||||
.trailing_icon(
|
||||
button(icon_cache_get("edit-clear-symbolic", 16))
|
||||
.on_press(Message::FindSearchValueChanged(String::new()))
|
||||
.style(style::Button::Icon)
|
||||
.into(),
|
||||
);
|
||||
let find_widget = widget::row::with_children(vec![
|
||||
text_input.into(),
|
||||
button(icon_cache_get("go-up-symbolic", 16))
|
||||
.on_press(Message::FindPrevious)
|
||||
.padding(space_xxs)
|
||||
.style(style::Button::Icon)
|
||||
.into(),
|
||||
button(icon_cache_get("go-down-symbolic", 16))
|
||||
.on_press(Message::FindNext)
|
||||
.padding(space_xxs)
|
||||
.style(style::Button::Icon)
|
||||
.into(),
|
||||
widget::horizontal_space(Length::Fill).into(),
|
||||
button(icon_cache_get("window-close-symbolic", 16))
|
||||
.on_press(Message::Find(None))
|
||||
.padding(space_xxs)
|
||||
.style(style::Button::Icon)
|
||||
.into(),
|
||||
])
|
||||
.align_items(Alignment::Center)
|
||||
.padding(space_xxs)
|
||||
.spacing(space_xxs);
|
||||
|
||||
tab_column = tab_column.push(
|
||||
widget::cosmic_container::container(find_widget)
|
||||
.layer(cosmic_theme::Layer::Primary),
|
||||
);
|
||||
}
|
||||
|
||||
let content: Element<_> = tab_column.into();
|
||||
|
||||
// Uncomment to debug layout:
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ pub fn menu_bar<'a>(config: &Config) -> Element<'a, Message> {
|
|||
menu_item(fl!("paste"), Message::Paste),
|
||||
menu_item(fl!("select-all"), Message::SelectAll),
|
||||
MenuTree::new(horizontal_rule(1)),
|
||||
menu_key(fl!("find"), "Ctrl + F", Message::Todo),
|
||||
menu_key(fl!("replace"), "Ctrl + H", Message::Todo),
|
||||
menu_item(fl!("find"), Message::Find(Some(false))),
|
||||
menu_item(fl!("replace"), Message::Find(Some(true))),
|
||||
menu_item(
|
||||
fl!("find-in-project"),
|
||||
Message::ToggleContextPage(ContextPage::ProjectSearch),
|
||||
|
|
|
|||
54
src/tab.rs
54
src/tab.rs
|
|
@ -199,4 +199,58 @@ impl EditorTab {
|
|||
fl!("new-document")
|
||||
}
|
||||
}
|
||||
|
||||
// Code adapted from cosmic-text ViEditor search
|
||||
pub fn search(&self, value: &str, forwards: bool) -> bool {
|
||||
let mut editor = self.editor.lock().unwrap();
|
||||
let mut cursor = editor.cursor();
|
||||
let start_line = cursor.line;
|
||||
if forwards {
|
||||
while cursor.line < editor.with_buffer(|buffer| buffer.lines.len()) {
|
||||
if let Some(index) = editor.with_buffer(|buffer| {
|
||||
buffer.lines[cursor.line]
|
||||
.text()
|
||||
.match_indices(value)
|
||||
.filter_map(|(i, _)| {
|
||||
if cursor.line != start_line || i > cursor.index {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}) {
|
||||
cursor.index = index;
|
||||
editor.set_cursor(cursor);
|
||||
return true;
|
||||
}
|
||||
|
||||
cursor.line += 1;
|
||||
}
|
||||
} else {
|
||||
cursor.line += 1;
|
||||
while cursor.line > 0 {
|
||||
cursor.line -= 1;
|
||||
|
||||
if let Some(index) = editor.with_buffer(|buffer| {
|
||||
buffer.lines[cursor.line]
|
||||
.text()
|
||||
.rmatch_indices(value)
|
||||
.filter_map(|(i, _)| {
|
||||
if cursor.line != start_line || i < cursor.index {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
}) {
|
||||
cursor.index = index;
|
||||
editor.set_cursor(cursor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,11 @@ use cosmic::{
|
|||
image,
|
||||
layout::{self, Layout},
|
||||
renderer::{self, Quad},
|
||||
widget::{self, tree, Widget},
|
||||
widget::{
|
||||
self,
|
||||
operation::{self, Operation, OperationOutputWrapper},
|
||||
tree, Id, Widget,
|
||||
},
|
||||
Shell,
|
||||
},
|
||||
};
|
||||
|
|
@ -29,10 +33,11 @@ use crate::{line_number::LineNumberKey, FONT_SYSTEM, LINE_NUMBER_CACHE, SWASH_CA
|
|||
pub struct TextBox<'a, Message> {
|
||||
editor: &'a Mutex<ViEditor<'static, 'static>>,
|
||||
metrics: Metrics,
|
||||
id: Option<Id>,
|
||||
padding: Padding,
|
||||
on_changed: Option<Message>,
|
||||
click_timing: Duration,
|
||||
context_menu: Option<Point>,
|
||||
has_context_menu: bool,
|
||||
on_context_menu: Option<Box<dyn Fn(Option<Point>) -> Message + 'a>>,
|
||||
line_numbers: bool,
|
||||
}
|
||||
|
|
@ -45,15 +50,21 @@ where
|
|||
Self {
|
||||
editor,
|
||||
metrics,
|
||||
id: None,
|
||||
padding: Padding::new(0.0),
|
||||
on_changed: None,
|
||||
click_timing: Duration::from_millis(500),
|
||||
context_menu: None,
|
||||
has_context_menu: false,
|
||||
on_context_menu: None,
|
||||
line_numbers: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: Id) -> Self {
|
||||
self.id = Some(id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||
self.padding = padding.into();
|
||||
self
|
||||
|
|
@ -69,8 +80,8 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn context_menu(mut self, position: Point) -> Self {
|
||||
self.context_menu = Some(position);
|
||||
pub fn has_context_menu(mut self, has_context_menu: bool) -> Self {
|
||||
self.has_context_menu = has_context_menu;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -252,6 +263,18 @@ where
|
|||
})
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
tree: &mut widget::Tree,
|
||||
_layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
operation: &mut dyn Operation<OperationOutputWrapper<Message>>,
|
||||
) {
|
||||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
operation.focusable(state, self.id.as_ref());
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &widget::Tree,
|
||||
|
|
@ -611,7 +634,10 @@ where
|
|||
Event::Keyboard(KeyEvent::KeyPressed {
|
||||
key_code,
|
||||
modifiers,
|
||||
}) => match key_code {
|
||||
}) => {
|
||||
// Only parse keys when focused
|
||||
if state.is_focused {
|
||||
match key_code {
|
||||
KeyCode::Left => {
|
||||
editor.action(Action::Motion(Motion::Left));
|
||||
status = Status::Captured;
|
||||
|
|
@ -669,21 +695,31 @@ where
|
|||
status = Status::Captured;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
|
||||
state.modifiers = modifiers;
|
||||
}
|
||||
Event::Keyboard(KeyEvent::CharacterReceived(character)) => {
|
||||
// Only parse keys when focused
|
||||
if state.is_focused {
|
||||
// Only parse keys when Super, Ctrl, and Alt are not pressed
|
||||
if !state.modifiers.logo() && !state.modifiers.control() && !state.modifiers.alt() {
|
||||
if !state.modifiers.logo()
|
||||
&& !state.modifiers.control()
|
||||
&& !state.modifiers.alt()
|
||||
{
|
||||
if !character.is_control() {
|
||||
editor.action(Action::Insert(character));
|
||||
}
|
||||
status = Status::Captured;
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::Mouse(MouseEvent::ButtonPressed(button)) => {
|
||||
if let Some(p) = cursor_position.position_in(layout.bounds()) {
|
||||
state.is_focused = true;
|
||||
|
||||
// Handle left click drag
|
||||
if let Button::Left = button {
|
||||
let x_logical = p.x - self.padding.left;
|
||||
|
|
@ -746,12 +782,13 @@ where
|
|||
|
||||
// Update context menu state
|
||||
if let Some(on_context_menu) = &self.on_context_menu {
|
||||
shell.publish((on_context_menu)(match self.context_menu {
|
||||
Some(_) => None,
|
||||
None => match button {
|
||||
shell.publish((on_context_menu)(if self.has_context_menu {
|
||||
None
|
||||
} else {
|
||||
match button {
|
||||
Button::Right => Some(p),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -869,6 +906,7 @@ pub struct State {
|
|||
click: Option<(ClickKind, Instant)>,
|
||||
dragging: Option<Dragging>,
|
||||
editor_offset_x: Cell<i32>,
|
||||
is_focused: bool,
|
||||
scale_factor: Cell<f32>,
|
||||
scroll_pixels: f32,
|
||||
scrollbar_rect: Cell<Rectangle<f32>>,
|
||||
|
|
@ -883,6 +921,7 @@ impl State {
|
|||
click: None,
|
||||
dragging: None,
|
||||
editor_offset_x: Cell::new(0),
|
||||
is_focused: false,
|
||||
scale_factor: Cell::new(1.0),
|
||||
scroll_pixels: 0.0,
|
||||
scrollbar_rect: Cell::new(Rectangle::default()),
|
||||
|
|
@ -890,3 +929,17 @@ impl State {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl operation::Focusable for State {
|
||||
fn is_focused(&self) -> bool {
|
||||
self.is_focused
|
||||
}
|
||||
|
||||
fn focus(&mut self) {
|
||||
self.is_focused = true;
|
||||
}
|
||||
|
||||
fn unfocus(&mut self) {
|
||||
self.is_focused = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue