mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 21:00:03 -04:00
Implement tabs as spaces
When we load a string in `TextEdit` we keep the tabs as they are.
Now depending on `tab_as_spaces` and `tab_size` properties of the
`TextEdit` we have the following situations:
- When `tab_as_spaces` is false, then when we press `TAB` a `TAB`
char in added to the string.
- When `tab_as_spaces` is true, then when we press `TAB` a chunck
of `tab_size` spaces is added to the string.
To config `tab_as_spaces` and `tab_size` to the values we want we
can use the folliwing helper functions:
- `TextEdit::tab_as_spaces()`
- `TextEdit::tab_size()`
Example usage:
```rust
ui.add(egui::TextEdit::multiline(&mut self.multiline_text_input)
.tab_as_spaces(self.tab_as_spaces)
.tab_size(self.tab_size));
```
Demo:
A better demo of this new functionality was implemented in the
misc widgets demo
What's left to do:
- Render actual `TAB` as a rect the size of `tab_size` spaces.
- Without default parameters values it seems a pain to implement.
I need to think if it is possible to avoid API breaking changes
for the font system. From my checks so far I need to pass at
least `tab_size` to the fonts system to know how big the glyph
for `TAB` will be.
This commit is contained in:
@@ -135,6 +135,8 @@ pub struct TextEdit<'t> {
|
||||
enabled: bool,
|
||||
desired_width: Option<f32>,
|
||||
desired_height_rows: usize,
|
||||
tab_as_spaces: bool,
|
||||
tab_size: usize,
|
||||
}
|
||||
impl<'t> TextEdit<'t> {
|
||||
pub fn cursor(ui: &Ui, id: Id) -> Option<CursorPair> {
|
||||
@@ -165,6 +167,8 @@ impl<'t> TextEdit<'t> {
|
||||
enabled: true,
|
||||
desired_width: None,
|
||||
desired_height_rows: 1,
|
||||
tab_as_spaces: true,
|
||||
tab_size: 4,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,9 +186,33 @@ impl<'t> TextEdit<'t> {
|
||||
enabled: true,
|
||||
desired_width: None,
|
||||
desired_height_rows: 4,
|
||||
tab_as_spaces: true,
|
||||
tab_size: 4,
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers if this widget will insert spaces instead of tab char
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ui.add(egui::TextEdit::multiline(&mut self.multiline_text_input)
|
||||
/// .tab_as_spaces(true));
|
||||
/// ```
|
||||
pub fn tab_as_spaces(mut self, b: bool) -> Self {
|
||||
self.tab_as_spaces = b;
|
||||
self
|
||||
}
|
||||
|
||||
/// Registers if this widget will insert spaces instead of tab char
|
||||
///
|
||||
/// ```rust, ignore
|
||||
/// ui.add(egui::TextEdit::multiline(&mut self.multiline_text_input)
|
||||
/// .tab_size(4));
|
||||
/// ```
|
||||
pub fn tab_size(mut self, size: usize) -> Self {
|
||||
self.tab_size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn id(mut self, id: Id) -> Self {
|
||||
self.id = Some(id);
|
||||
self
|
||||
@@ -297,11 +325,10 @@ impl<'t> TextEdit<'t> {
|
||||
enabled,
|
||||
desired_width,
|
||||
desired_height_rows,
|
||||
tab_as_spaces,
|
||||
tab_size,
|
||||
} = self;
|
||||
|
||||
// Tabs are represented as 4 spaces for now
|
||||
*text = text.replace("\t", " ");
|
||||
|
||||
let text_style = text_style.unwrap_or_else(|| ui.style().body_text_style);
|
||||
let font = &ui.fonts()[text_style];
|
||||
let line_spacing = font.row_height();
|
||||
@@ -453,11 +480,20 @@ impl<'t> TextEdit<'t> {
|
||||
} => {
|
||||
if multiline {
|
||||
let mut ccursor = delete_selected(text, &cursorp);
|
||||
insert_text(&mut ccursor, text, "\t");
|
||||
|
||||
// Because we add a tab and the tab is represented
|
||||
// as 4 spaces we must advance the cursor
|
||||
Some(CCursorPair::one(ccursor + 3))
|
||||
if tab_as_spaces {
|
||||
let mut spaces = String::with_capacity(tab_size);
|
||||
|
||||
for _ in 0..tab_size {
|
||||
spaces.push(' ');
|
||||
}
|
||||
|
||||
insert_text(&mut ccursor, text, &spaces);
|
||||
} else {
|
||||
insert_text(&mut ccursor, text, "\t");
|
||||
}
|
||||
|
||||
Some(CCursorPair::one(ccursor))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ pub struct WidgetGallery {
|
||||
radio: Enum,
|
||||
scalar: f32,
|
||||
string: String,
|
||||
string_multiline: String,
|
||||
color: egui::Color32,
|
||||
}
|
||||
|
||||
@@ -25,7 +24,6 @@ impl Default for WidgetGallery {
|
||||
radio: Enum::First,
|
||||
scalar: 42.0,
|
||||
string: Default::default(),
|
||||
string_multiline: Default::default(),
|
||||
color: egui::Color32::LIGHT_BLUE.linear_multiply(0.5),
|
||||
}
|
||||
}
|
||||
@@ -87,7 +85,6 @@ impl WidgetGallery {
|
||||
radio,
|
||||
scalar,
|
||||
string,
|
||||
string_multiline,
|
||||
color,
|
||||
} = self;
|
||||
|
||||
@@ -109,10 +106,6 @@ impl WidgetGallery {
|
||||
ui.add(egui::TextEdit::singleline(string).hint_text("Write something here"));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(doc_link_label("TextEdit", "TextEdit,text_edit"));
|
||||
ui.add(egui::TextEdit::multiline(string_multiline).hint_text("Write something here"));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(doc_link_label("Button", "button"));
|
||||
if ui.button("Click me!").clicked() {
|
||||
*boolean = !*boolean;
|
||||
|
||||
@@ -23,6 +23,8 @@ pub struct Widgets {
|
||||
angle: f32,
|
||||
color: Color32,
|
||||
single_line_text_input: String,
|
||||
tab_size: usize,
|
||||
tab_as_spaces: bool,
|
||||
multiline_text_input: String,
|
||||
}
|
||||
|
||||
@@ -35,7 +37,16 @@ impl Default for Widgets {
|
||||
angle: std::f32::consts::TAU / 3.0,
|
||||
color: (Rgba::from_rgb(0.0, 1.0, 0.5) * 0.75).into(),
|
||||
single_line_text_input: "Hello World!".to_owned(),
|
||||
multiline_text_input: "Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.\nThis is the start of the next paragraph.\n\nClick me to edit me!".to_owned(),
|
||||
|
||||
tab_size: 4,
|
||||
tab_as_spaces: true,
|
||||
multiline_text_input: r#"Text can both be so wide that it needs a line break, but you can also add manual line break by pressing enter, creating new paragraphs.
|
||||
This is the start of the next paragraph.
|
||||
|
||||
Existing tabs are kept as tabs.
|
||||
Use the configs above to set how new tabs should be handled and test by pressing Tab.
|
||||
|
||||
Click me to edit me!"#.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,7 +144,22 @@ impl Widgets {
|
||||
}
|
||||
});
|
||||
|
||||
ui.label("Multiline text input:");
|
||||
ui.text_edit_multiline(&mut self.multiline_text_input);
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Multiline text input:");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.add(egui::Slider::usize(&mut self.tab_size, 2..=8).text("Tab size"));
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.checkbox(&mut self.tab_as_spaces, "Tabs as spaces");
|
||||
});
|
||||
|
||||
ui.add(
|
||||
egui::TextEdit::multiline(&mut self.multiline_text_input)
|
||||
.tab_as_spaces(self.tab_as_spaces)
|
||||
.tab_size(self.tab_size),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user