Implement find and improvements for focus

This commit is contained in:
Jeremy Soller 2024-01-09 12:40:29 -07:00
parent 81a0bbc094
commit f3a8fef8f8
No known key found for this signature in database
GPG key ID: DCFCA852D3906975
10 changed files with 358 additions and 105 deletions

32
Cargo.lock generated
View file

@ -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",

View file

@ -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

View 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

View 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

View file

@ -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);

View file

@ -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);

View file

@ -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:

View file

@ -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),

View file

@ -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
}
}

View file

@ -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,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<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;
}
}