From 8bc88c9bf403426e5e1b33b41c996cfd99d71fa2 Mon Sep 17 00:00:00 2001 From: lictex_ Date: Tue, 7 Feb 2023 17:52:48 +0800 Subject: [PATCH] make dragvalue textedit style consistent with button (#2649) * make dragvalue textedit style consistent with button * fix comments & fix wrong interactive cursor pos * * apply button_padding to textedit * support vertical align * add same min size as button to avoid unintented height shrink --- crates/egui/src/widgets/drag_value.rs | 8 ++- crates/egui/src/widgets/text_edit/builder.rs | 64 +++++++++++++++++--- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/crates/egui/src/widgets/drag_value.rs b/crates/egui/src/widgets/drag_value.rs index 2892538a..59507be2 100644 --- a/crates/egui/src/widgets/drag_value.rs +++ b/crates/egui/src/widgets/drag_value.rs @@ -461,14 +461,18 @@ impl<'a> Widget for DragValue<'a> { // some clones below are redundant if AccessKit is disabled #[allow(clippy::redundant_clone)] let mut response = if is_kb_editing { - let button_width = ui.spacing().interact_size.x; let mut value_text = ui .memory_mut(|mem| mem.drag_value.edit_string.take()) .unwrap_or_else(|| value_text.clone()); let response = ui.add( TextEdit::singleline(&mut value_text) + .clip_text(false) + .horizontal_align(ui.layout().horizontal_align()) + .vertical_align(ui.layout().vertical_align()) + .margin(ui.spacing().button_padding) + .min_size(ui.spacing().interact_size) .id(id) - .desired_width(button_width) + .desired_width(ui.spacing().interact_size.x) .font(text_style), ); let parsed_value = match custom_parser { diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 68cc2aae..7467c11d 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -67,6 +67,9 @@ pub struct TextEdit<'t> { desired_height_rows: usize, lock_focus: bool, cursor_at_end: bool, + min_size: Vec2, + align: Align2, + clip_text: bool, } impl<'t> WidgetWithState for TextEdit<'t> { @@ -89,6 +92,7 @@ impl<'t> TextEdit<'t> { Self { desired_height_rows: 1, multiline: false, + clip_text: true, ..Self::multiline(text) } } @@ -112,6 +116,9 @@ impl<'t> TextEdit<'t> { desired_height_rows: 4, lock_focus: false, cursor_at_end: true, + min_size: Vec2::ZERO, + align: Align2::LEFT_TOP, + clip_text: false, } } @@ -269,6 +276,37 @@ impl<'t> TextEdit<'t> { self.cursor_at_end = b; self } + + /// When `true` (default), overflowing text will be clipped. + /// + /// When `false`, widget width will expand to make all text visible. + /// + /// This only works for singleline [`TextEdit`]. + pub fn clip_text(mut self, b: bool) -> Self { + // always show everything in multiline + if !self.multiline { + self.clip_text = b; + } + self + } + + /// Set the horizontal align of the inner text. + pub fn horizontal_align(mut self, align: Align) -> Self { + self.align.0[0] = align; + self + } + + /// Set the vertical align of the inner text. + pub fn vertical_align(mut self, align: Align) -> Self { + self.align.0[1] = align; + self + } + + /// Set the minimum size of the [`TextEdit`]. + pub fn min_size(mut self, min_size: Vec2) -> Self { + self.min_size = min_size; + self + } } // ---------------------------------------------------------------------------- @@ -364,13 +402,16 @@ impl<'t> TextEdit<'t> { layouter, password, frame: _, - margin: _, + margin, multiline, interactive, desired_width, desired_height_rows, lock_focus, cursor_at_end, + min_size, + align, + clip_text, } = self; let text_color = text_color @@ -389,7 +430,7 @@ impl<'t> TextEdit<'t> { available_width } else { desired_width.min(available_width) - }; + } - margin.x * 2.0; let font_id_clone = font_id.clone(); let mut default_layouter = move |ui: &Ui, text: &str, wrap_width: f32| { @@ -406,13 +447,14 @@ impl<'t> TextEdit<'t> { let mut galley = layouter(ui, text.as_str(), wrap_width); - let desired_width = if multiline { - galley.size().x.max(wrap_width) // always show everything in multiline + let desired_width = if clip_text { + wrap_width // visual clipping with scroll in singleline input. } else { - wrap_width // visual clipping with scroll in singleline input. TODO(emilk): opt-in/out? + galley.size().x.max(wrap_width) }; let desired_height = (desired_height_rows.at_least(1) as f32) * row_height; - let desired_size = vec2(desired_width, galley.size().y.max(desired_height)); + let desired_size = vec2(desired_width, galley.size().y.max(desired_height)) + .at_least(min_size - margin * 2.0); let (auto_id, rect) = ui.allocate_space(desired_size); @@ -547,10 +589,14 @@ impl<'t> TextEdit<'t> { cursor_range = Some(new_cursor_range); } - let mut text_draw_pos = response.rect.min; + let mut text_draw_pos = align + .align_size_within_rect(galley.size(), response.rect) + .intersect(response.rect) // limit pos to the response rect area + .min; + let align_offset = response.rect.left() - text_draw_pos.x; // Visual clipping for singleline text editor with text larger than width - if !multiline { + if clip_text && align_offset == 0.0 { let cursor_pos = match (cursor_range, ui.memory(|mem| mem.has_focus(id))) { (Some(cursor_range), true) => galley.pos_from_cursor(&cursor_range.primary).min.x, _ => 0.0, @@ -573,6 +619,8 @@ impl<'t> TextEdit<'t> { state.singleline_offset = offset_x; text_draw_pos -= vec2(offset_x, 0.0); + } else { + state.singleline_offset = align_offset; } let selection_changed = if let (Some(cursor_range), Some(prev_cursor_range)) =