1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

Support Cmd+A ^W ^U ^K and shift-click

This commit is contained in:
Emil Ernerfeldt
2020-11-15 14:21:21 +01:00
parent b920822b6b
commit fe0d159324
8 changed files with 174 additions and 77 deletions

View File

@@ -33,6 +33,9 @@ pub struct RawInput {
/// Time in seconds. Relative to whatever. Used for animations.
pub time: f64,
/// Which modifier keys are down at the start of the frame?
pub modifiers: Modifiers,
/// In-order events received this frame
pub events: Vec<Event>,
}
@@ -47,6 +50,7 @@ impl RawInput {
screen_size: self.screen_size,
pixels_per_point: self.pixels_per_point,
time: self.time,
modifiers: self.modifiers,
events: std::mem::take(&mut self.events),
}
}
@@ -80,6 +84,9 @@ pub struct InputState {
/// Should be set to the expected time between frames when painting at vsync speeds.
pub predicted_dt: f32,
/// Which modifier keys are down at the start of the frame?
pub modifiers: Modifiers,
/// In-order events received this frame
pub events: Vec<Event>,
}
@@ -200,6 +207,12 @@ pub enum Key {
PageDown,
PageUp,
Tab,
A, // Used for cmd+A (select All)
K, // Used for ctrl+K (delete text after cursor)
U, // Used for ctrl+U (delete text before cursor)
W, // Used for ctrl+W (delete previous word)
Z, // Used for cmd+Z (undo)
}
impl InputState {
@@ -214,7 +227,8 @@ impl InputState {
pixels_per_point: new.pixels_per_point.or(self.pixels_per_point),
time: new.time,
unstable_dt,
predicted_dt: 1.0 / 60.0, // TODO: remove this hack
predicted_dt: 1.0 / 60.0, // TODO: remove this hack
modifiers: new.modifiers,
events: new.events.clone(), // TODO: remove clone() and use raw.events
raw: new,
}
@@ -357,6 +371,7 @@ impl RawInput {
screen_size,
pixels_per_point,
time,
modifiers,
events,
} = self;
@@ -371,6 +386,7 @@ impl RawInput {
"Also called HDPI factor.\nNumber of physical pixels per each logical pixel.",
);
ui.label(format!("time: {:.3} s", time));
ui.label(format!("modifiers: {:#?}", modifiers));
ui.label(format!("events: {:?}", events))
.on_hover_text("key presses etc");
}
@@ -387,6 +403,7 @@ impl InputState {
time,
unstable_dt,
predicted_dt,
modifiers,
events,
} = self;
@@ -411,6 +428,7 @@ impl InputState {
1e3 * unstable_dt
));
ui.label(format!("expected dt: {:.1} ms", 1e3 * predicted_dt));
ui.label(format!("modifiers: {:#?}", modifiers));
ui.label(format!("events: {:?}", events))
.on_hover_text("key presses etc");
}

View File

@@ -192,6 +192,10 @@ impl Row {
*self.x_offsets.last().unwrap()
}
pub fn height(&self) -> f32 {
self.y_max - self.y_min
}
/// Closest char at the desired x coordinate.
/// Returns something in the range `[0, char_count_excluding_newline()]`.
pub fn char_at(&self, desired_x: f32) -> usize {
@@ -233,7 +237,7 @@ impl Galley {
fn end_pos(&self) -> Rect {
if let Some(row) = self.rows.last() {
let x = row.max_x();
return Rect::from_min_max(pos2(x, row.y_min), pos2(x, row.y_max));
Rect::from_min_max(pos2(x, row.y_min), pos2(x, row.y_max))
} else {
// Empty galley
Rect::from_min_max(pos2(0.0, 0.0), pos2(0.0, 0.0))
@@ -636,6 +640,13 @@ impl Galley {
#[test]
fn test_text_layout() {
impl PartialEq for Cursor {
fn eq(&self, other: &Cursor) -> bool {
(self.ccursor, self.rcursor, self.pcursor)
== (other.ccursor, other.rcursor, other.pcursor)
}
}
use crate::mutex::Mutex;
use crate::paint::{font::Font, *};

View File

@@ -267,7 +267,15 @@ impl<'t> Widget for TextEdit<'t> {
});
} else if response.hovered && ui.input().mouse.pressed {
ui.memory().request_kb_focus(id);
state.cursorp = Some(CursorPair::one(cursor_at_mouse));
if ui.input().modifiers.shift {
if let Some(cursorp) = &mut state.cursorp {
cursorp.primary = cursor_at_mouse;
} else {
state.cursorp = Some(CursorPair::one(cursor_at_mouse));
}
} else {
state.cursorp = Some(CursorPair::one(cursor_at_mouse));
}
} else if ui.input().mouse.down && response.active {
if let Some(cursorp) = &mut state.cursorp {
cursorp.primary = cursor_at_mouse;
@@ -332,7 +340,7 @@ impl<'t> Widget for TextEdit<'t> {
&& text_to_insert != "\n"
&& text_to_insert != "\r"
{
let mut ccursor = delete_selected(text, &cursorp).into();
let mut ccursor = delete_selected(text, &cursorp);
insert_text(&mut ccursor, text, text_to_insert);
Some(CCursorPair::one(ccursor))
} else {
@@ -345,7 +353,7 @@ impl<'t> Widget for TextEdit<'t> {
..
} => {
if multiline {
let mut ccursor = delete_selected(text, &cursorp).into();
let mut ccursor = delete_selected(text, &cursorp);
insert_text(&mut ccursor, text, "\n");
Some(CCursorPair::one(ccursor))
} else {
@@ -452,7 +460,12 @@ fn paint_cursor_selection(
let right = if ri == max.row {
row.x_offset(max.column)
} else {
row.max_x()
let newline_size = if row.ends_with_newline {
row.height() / 2.0 // visualize that we select the newline
} else {
0.0
};
row.max_x() + newline_size
};
let rect = Rect::from_min_max(pos + vec2(left, row.y_min), pos + vec2(right, row.y_max));
ui.painter().rect_filled(rect, 0.0, color);
@@ -499,7 +512,7 @@ fn byte_index_from_char_index(s: &str, char_index: usize) -> usize {
return bi;
}
}
return s.len();
s.len()
}
fn insert_text(ccursor: &mut CCursor, text: &mut String, text_to_insert: &str) {
@@ -555,12 +568,12 @@ fn delete_next_char(text: &mut String, ccursor: CCursor) -> CCursor {
}
fn delete_previous_word(text: &mut String, max_ccursor: CCursor) -> CCursor {
let min_ccursor = ccursor_previous_word(&text, max_ccursor);
let min_ccursor = ccursor_previous_word(text, max_ccursor);
delete_selected_ccursor_range(text, [min_ccursor, max_ccursor])
}
fn delete_next_word(text: &mut String, min_ccursor: CCursor) -> CCursor {
let max_ccursor = ccursor_next_word(&text, min_ccursor);
let max_ccursor = ccursor_next_word(text, min_ccursor);
delete_selected_ccursor_range(text, [min_ccursor, max_ccursor])
}
@@ -610,10 +623,6 @@ fn on_key_press(
key: Key,
modifiers: &Modifiers,
) -> Option<CCursorPair> {
// TODO: ctrl-U to clear paragraph before the cursor
// TODO: ctrl-W to delete previous word
// TODO: cmd-A to select all
match key {
Key::Backspace => {
let ccursor = if modifiers.mac_cmd {
@@ -650,6 +659,31 @@ fn on_key_press(
Some(CCursorPair::one(ccursor))
}
Key::A if modifiers.command => {
// select all
*cursorp = CursorPair::two(Cursor::default(), galley.end());
None
}
Key::K if modifiers.ctrl => {
let ccursor = delete_paragraph_after_cursor(text, galley, cursorp);
Some(CCursorPair::one(ccursor))
}
Key::U if modifiers.ctrl => {
let ccursor = delete_paragraph_before_cursor(text, galley, cursorp);
Some(CCursorPair::one(ccursor))
}
Key::W if modifiers.ctrl => {
let ccursor = if let Some(cursor) = cursorp.single() {
delete_previous_word(text, cursor.ccursor)
} else {
delete_selected(text, cursorp)
};
Some(CCursorPair::one(ccursor))
}
Key::ArrowLeft | Key::ArrowRight | Key::ArrowUp | Key::ArrowDown | Key::Home | Key::End => {
move_single_cursor(&mut cursorp.primary, galley, key, modifiers);
if !modifiers.shift {
@@ -658,9 +692,7 @@ fn on_key_press(
None
}
Key::Enter | Key::Escape => unreachable!("Handled outside this function"),
Key::Insert | Key::PageDown | Key::PageUp | Key::Tab => None,
_ => None,
}
}