Decrease indentation with shift-tab
This commit is contained in:
parent
bf8ce774cc
commit
66122e4c9a
4 changed files with 80 additions and 6 deletions
|
@ -501,12 +501,16 @@ impl<'t> TextEdit<'t> {
|
||||||
Event::Key {
|
Event::Key {
|
||||||
key: Key::Tab,
|
key: Key::Tab,
|
||||||
pressed: true,
|
pressed: true,
|
||||||
..
|
modifiers,
|
||||||
} => {
|
} => {
|
||||||
if multiline && ui.memory().has_lock_focus(id) {
|
if multiline && ui.memory().has_lock_focus(id) {
|
||||||
let mut ccursor = delete_selected(text, &cursorp);
|
let mut ccursor = delete_selected(text, &cursorp);
|
||||||
|
if modifiers.shift {
|
||||||
|
// TODO: support removing indentation over a selection?
|
||||||
|
decrease_identation(&mut ccursor, text);
|
||||||
|
} else {
|
||||||
insert_text(&mut ccursor, text, "\t");
|
insert_text(&mut ccursor, text, "\t");
|
||||||
|
}
|
||||||
Some(CCursorPair::one(ccursor))
|
Some(CCursorPair::one(ccursor))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1043,3 +1047,61 @@ fn next_word_boundary_char_index(it: impl Iterator<Item = char>, mut index: usiz
|
||||||
fn is_word_char(c: char) -> bool {
|
fn is_word_char(c: char) -> bool {
|
||||||
c.is_ascii_alphanumeric() || c == '_'
|
c.is_ascii_alphanumeric() || c == '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accepts and returns character offset (NOT byte offset!).
|
||||||
|
fn find_line_start(text: &str, current_index: CCursor) -> CCursor {
|
||||||
|
// We know that new lines, '\n', are a single byte char, but we have to
|
||||||
|
// work with char offsets because before the new line there may be any
|
||||||
|
// number of multi byte chars.
|
||||||
|
// We need to know the char index to be able to correctly set the cursor
|
||||||
|
// later.
|
||||||
|
let chars_count = text.chars().count();
|
||||||
|
|
||||||
|
let position = text
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.skip(chars_count - current_index.index)
|
||||||
|
.position(|x| x == '\n');
|
||||||
|
|
||||||
|
match position {
|
||||||
|
Some(pos) => CCursor::new(current_index.index - pos),
|
||||||
|
None => CCursor::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease_identation(ccursor: &mut CCursor, text: &mut String) {
|
||||||
|
let mut new_text = String::with_capacity(text.len());
|
||||||
|
|
||||||
|
let line_start = find_line_start(text, *ccursor);
|
||||||
|
|
||||||
|
let mut char_it = text.chars().peekable();
|
||||||
|
for _ in 0..line_start.index {
|
||||||
|
let c = char_it.next().unwrap();
|
||||||
|
new_text.push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chars_removed = 0;
|
||||||
|
while let Some(&c) = char_it.peek() {
|
||||||
|
if c == '\t' {
|
||||||
|
char_it.next();
|
||||||
|
chars_removed += 1;
|
||||||
|
break;
|
||||||
|
} else if c == ' ' {
|
||||||
|
char_it.next();
|
||||||
|
chars_removed += 1;
|
||||||
|
if chars_removed == text::TAB_SIZE {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_text.extend(char_it);
|
||||||
|
|
||||||
|
*text = new_text;
|
||||||
|
|
||||||
|
if *ccursor != line_start {
|
||||||
|
*ccursor -= chars_removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -51,6 +51,18 @@ impl std::ops::Sub<usize> for CCursor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::AddAssign<usize> for CCursor {
|
||||||
|
fn add_assign(&mut self, rhs: usize) {
|
||||||
|
self.index = self.index.saturating_add(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::SubAssign<usize> for CCursor {
|
||||||
|
fn sub_assign(&mut self, rhs: usize) {
|
||||||
|
self.index = self.index.saturating_sub(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Row Cursor
|
/// Row Cursor
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
|
|
@ -127,7 +127,7 @@ impl FontImpl {
|
||||||
|
|
||||||
if c == '\t' {
|
if c == '\t' {
|
||||||
if let Some(space) = self.glyph_info(' ') {
|
if let Some(space) = self.glyph_info(' ') {
|
||||||
glyph_info.advance_width = crate::text::TAB_SIZE * space.advance_width;
|
glyph_info.advance_width = crate::text::TAB_SIZE as f32 * space.advance_width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ mod font;
|
||||||
mod fonts;
|
mod fonts;
|
||||||
mod galley;
|
mod galley;
|
||||||
|
|
||||||
/// Default size for a `\t` character.
|
/// One `\t` character is this many spaces wide.
|
||||||
pub const TAB_SIZE: f32 = 4.0;
|
pub const TAB_SIZE: usize = 4;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
|
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},
|
||||||
|
|
Loading…
Reference in a new issue