diff --git a/Cargo.lock b/Cargo.lock
index eeb97d2..af948c2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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",
diff --git a/i18n/en/cosmic_edit.ftl b/i18n/en/cosmic_edit.ftl
index b63182a..11a9107 100644
--- a/i18n/en/cosmic_edit.ftl
+++ b/i18n/en/cosmic_edit.ftl
@@ -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
diff --git a/res/icons/edit-clear-symbolic.svg b/res/icons/edit-clear-symbolic.svg
new file mode 100644
index 0000000..bcf0a70
--- /dev/null
+++ b/res/icons/edit-clear-symbolic.svg
@@ -0,0 +1,3 @@
+
diff --git a/res/icons/go-up-symbolic.svg b/res/icons/go-up-symbolic.svg
new file mode 100644
index 0000000..3747d43
--- /dev/null
+++ b/res/icons/go-up-symbolic.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/config.rs b/src/config.rs
index 8b3c396..422b72d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -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);
diff --git a/src/icon_cache.rs b/src/icon_cache.rs
index 2008b76..aeca7db 100644
--- a/src/icon_cache.rs
+++ b/src/icon_cache.rs
@@ -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);
diff --git a/src/main.rs b/src/main.rs
index 248afad..cd53dfd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -181,6 +181,11 @@ pub enum Message {
Cut,
DefaultFont(usize),
DefaultFontSize(usize),
+ Find(Option),
+ FindNext,
+ FindPrevious,
+ FindReplaceValueChanged(String),
+ FindSearchValueChanged(String),
GitProjectStatus(Vec<(String, PathBuf, Vec)>),
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,
theme_names: Vec,
context_page: ContextPage,
+ text_box_id: widget::Id,
+ find_opt: Option,
+ find_replace_value: String,
+ find_search_id: widget::Id,
+ find_search_value: String,
git_project_status: Option)>>,
projects: Vec<(String, PathBuf)>,
project_search_id: widget::Id,
@@ -440,6 +457,21 @@ impl App {
self.update_config()
}
+ fn update_focus(&self) -> Command {
+ 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 {
@@ -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 {
+ // Focus correct widget
+ self.update_focus()
+ }
+
+ //TODO: currently the first escape unfocuses, and the second calls this function
+ fn on_escape(&mut self) -> Command {
+ 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 {
// Toggle open state and get clone of node data
let node_opt = match self.nav_model.data_mut::(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:
diff --git a/src/menu.rs b/src/menu.rs
index d3e0eb3..54fea29 100644
--- a/src/menu.rs
+++ b/src/menu.rs
@@ -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),
diff --git a/src/tab.rs b/src/tab.rs
index a3bf73e..e61dcfb 100644
--- a/src/tab.rs
+++ b/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
+ }
}
diff --git a/src/text_box.rs b/src/text_box.rs
index dcdc836..8ec80fc 100644
--- a/src/text_box.rs
+++ b/src/text_box.rs
@@ -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>,
metrics: Metrics,
+ id: Option,
padding: Padding,
on_changed: Option,
click_timing: Duration,
- context_menu: Option,
+ has_context_menu: bool,
on_context_menu: Option) -> 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>(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>,
+ ) {
+ let state = tree.state.downcast_mut::();
+
+ operation.focusable(state, self.id.as_ref());
+ }
+
fn mouse_interaction(
&self,
tree: &widget::Tree,
@@ -611,79 +634,92 @@ where
Event::Keyboard(KeyEvent::KeyPressed {
key_code,
modifiers,
- }) => match key_code {
- KeyCode::Left => {
- editor.action(Action::Motion(Motion::Left));
- status = Status::Captured;
- }
- KeyCode::Right => {
- editor.action(Action::Motion(Motion::Right));
- status = Status::Captured;
- }
- KeyCode::Up => {
- editor.action(Action::Motion(Motion::Up));
- status = Status::Captured;
- }
- KeyCode::Down => {
- editor.action(Action::Motion(Motion::Down));
- status = Status::Captured;
- }
- KeyCode::Home => {
- editor.action(Action::Motion(Motion::Home));
- status = Status::Captured;
- }
- KeyCode::End => {
- editor.action(Action::Motion(Motion::End));
- status = Status::Captured;
- }
- KeyCode::PageUp => {
- editor.action(Action::Motion(Motion::PageUp));
- status = Status::Captured;
- }
- KeyCode::PageDown => {
- editor.action(Action::Motion(Motion::PageDown));
- status = Status::Captured;
- }
- KeyCode::Escape => {
- editor.action(Action::Escape);
- status = Status::Captured;
- }
- KeyCode::Enter => {
- editor.action(Action::Enter);
- status = Status::Captured;
- }
- KeyCode::Backspace => {
- editor.action(Action::Backspace);
- status = Status::Captured;
- }
- KeyCode::Delete => {
- editor.action(Action::Delete);
- status = Status::Captured;
- }
- KeyCode::Tab => {
- if modifiers.shift() {
- editor.action(Action::Unindent);
- } else {
- editor.action(Action::Indent);
+ }) => {
+ // Only parse keys when focused
+ if state.is_focused {
+ match key_code {
+ KeyCode::Left => {
+ editor.action(Action::Motion(Motion::Left));
+ status = Status::Captured;
+ }
+ KeyCode::Right => {
+ editor.action(Action::Motion(Motion::Right));
+ status = Status::Captured;
+ }
+ KeyCode::Up => {
+ editor.action(Action::Motion(Motion::Up));
+ status = Status::Captured;
+ }
+ KeyCode::Down => {
+ editor.action(Action::Motion(Motion::Down));
+ status = Status::Captured;
+ }
+ KeyCode::Home => {
+ editor.action(Action::Motion(Motion::Home));
+ status = Status::Captured;
+ }
+ KeyCode::End => {
+ editor.action(Action::Motion(Motion::End));
+ status = Status::Captured;
+ }
+ KeyCode::PageUp => {
+ editor.action(Action::Motion(Motion::PageUp));
+ status = Status::Captured;
+ }
+ KeyCode::PageDown => {
+ editor.action(Action::Motion(Motion::PageDown));
+ status = Status::Captured;
+ }
+ KeyCode::Escape => {
+ editor.action(Action::Escape);
+ status = Status::Captured;
+ }
+ KeyCode::Enter => {
+ editor.action(Action::Enter);
+ status = Status::Captured;
+ }
+ KeyCode::Backspace => {
+ editor.action(Action::Backspace);
+ status = Status::Captured;
+ }
+ KeyCode::Delete => {
+ editor.action(Action::Delete);
+ status = Status::Captured;
+ }
+ KeyCode::Tab => {
+ if modifiers.shift() {
+ editor.action(Action::Unindent);
+ } else {
+ editor.action(Action::Indent);
+ }
+ status = Status::Captured;
+ }
+ _ => (),
}
- status = Status::Captured;
}
- _ => (),
- },
+ }
Event::Keyboard(KeyEvent::ModifiersChanged(modifiers)) => {
state.modifiers = modifiers;
}
Event::Keyboard(KeyEvent::CharacterReceived(character)) => {
- // Only parse keys when Super, Ctrl, and Alt are not pressed
- if !state.modifiers.logo() && !state.modifiers.control() && !state.modifiers.alt() {
- if !character.is_control() {
- editor.action(Action::Insert(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 !character.is_control() {
+ editor.action(Action::Insert(character));
+ }
+ status = Status::Captured;
}
- 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,
editor_offset_x: Cell,
+ is_focused: bool,
scale_factor: Cell,
scroll_pixels: f32,
scrollbar_rect: Cell>,
@@ -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;
+ }
+}