Browse Source

Decrease indentation with shift-tab

pull/352/head
Emil Ernerfeldt 4 years ago
parent
commit
66122e4c9a
  1. 66
      egui/src/widgets/text_edit.rs
  2. 12
      epaint/src/text/cursor.rs
  3. 2
      epaint/src/text/font.rs
  4. 4
      epaint/src/text/mod.rs

66
egui/src/widgets/text_edit.rs

@ -501,12 +501,16 @@ impl<'t> TextEdit<'t> {
Event::Key {
key: Key::Tab,
pressed: true,
..
modifiers,
} => {
if multiline && ui.memory().has_lock_focus(id) {
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");
}
Some(CCursorPair::one(ccursor))
} else {
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 {
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;
}
}

12
epaint/src/text/cursor.rs

@ -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
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]

2
epaint/src/text/font.rs

@ -127,7 +127,7 @@ impl FontImpl {
if c == '\t' {
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;
}
}

4
epaint/src/text/mod.rs

@ -5,8 +5,8 @@ mod font;
mod fonts;
mod galley;
/// Default size for a `\t` character.
pub const TAB_SIZE: f32 = 4.0;
/// One `\t` character is this many spaces wide.
pub const TAB_SIZE: usize = 4;
pub use {
fonts::{FontDefinitions, FontFamily, Fonts, TextStyle},

Loading…
Cancel
Save