diff --git a/crates/story/examples/html.rs b/crates/story/examples/html.rs
index c4296143..2aa3cb02 100644
--- a/crates/story/examples/html.rs
+++ b/crates/story/examples/html.rs
@@ -1,5 +1,5 @@
use gpui::*;
-use gpui_component::{input::TextInput, text::TextView, ActiveTheme as _};
+use gpui_component::{h_flex, input::TextInput, text::TextView, ActiveTheme as _};
use story::Assets;
pub struct Example {
@@ -14,8 +14,9 @@ impl Example {
let text_input = cx.new(|cx| {
TextInput::new(window, cx)
.multi_line()
- .rows(50)
- .placeholder("Input your HTML here...")
+ .appearance(false)
+ .h_full()
+ .placeholder("Enter your HTML here...")
});
let _subscribe = cx.subscribe(
@@ -42,9 +43,7 @@ impl Example {
impl Render for Example {
fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement {
- div()
- .flex()
- .flex_row()
+ h_flex()
.h_full()
.child(
div()
@@ -53,7 +52,6 @@ impl Render for Example {
.w_1_2()
.border_r_1()
.border_color(cx.theme().border)
- .flex_1()
.child(self.text_input.clone()),
)
.child(
@@ -62,7 +60,6 @@ impl Render for Example {
.h_full()
.w_1_2()
.p_5()
- .flex_1()
.overflow_y_scroll()
.child(TextView::html("preview", self.text_input.read(cx).text())),
)
diff --git a/crates/story/examples/markdown.rs b/crates/story/examples/markdown.rs
index 103a82d7..84f66776 100644
--- a/crates/story/examples/markdown.rs
+++ b/crates/story/examples/markdown.rs
@@ -1,14 +1,35 @@
use gpui::*;
-use gpui_component::{text::TextView, ActiveTheme as _};
+use gpui_component::{input::TextInput, text::TextView, ActiveTheme as _};
use story::Assets;
-pub struct Example {}
+pub struct Example {
+ text_input: Entity,
+}
const EXAMPLE: &str = include_str!("./markdown.md");
impl Example {
- pub fn new(_: &mut Window, _: &mut Context) -> Self {
- Self {}
+ pub fn new(window: &mut Window, cx: &mut Context) -> Self {
+ let text_input = cx.new(|cx| {
+ TextInput::new(window, cx)
+ .multi_line()
+ .appearance(false)
+ .h_full()
+ .placeholder("Enter your Markdown here...")
+ });
+
+ let _subscribe = cx.subscribe(
+ &text_input,
+ |_, _, _: &gpui_component::input::InputEvent, cx| {
+ cx.notify();
+ },
+ );
+
+ _ = text_input.update(cx, |input, cx| {
+ input.set_text(EXAMPLE, window, cx);
+ });
+
+ Self { text_input }
}
fn view(window: &mut Window, cx: &mut App) -> Entity {
@@ -30,9 +51,7 @@ impl Render for Example {
.border_r_1()
.border_color(cx.theme().border)
.flex_1()
- .p_5()
- .overflow_y_scroll()
- .child(EXAMPLE),
+ .child(self.text_input.clone()),
)
.child(
div()
@@ -42,7 +61,10 @@ impl Render for Example {
.p_5()
.flex_1()
.overflow_y_scroll()
- .child(TextView::markdown("preview", EXAMPLE)),
+ .child(TextView::markdown(
+ "preview",
+ self.text_input.read(cx).text(),
+ )),
)
}
}
diff --git a/crates/story/src/input_story.rs b/crates/story/src/input_story.rs
index f13a5f7a..7f7cbc33 100644
--- a/crates/story/src/input_story.rs
+++ b/crates/story/src/input_story.rs
@@ -69,7 +69,7 @@ impl InputStory {
fn new(window: &mut Window, cx: &mut Context) -> Self {
let input1 = cx.new(|cx| {
- let mut input = TextInput::new(window, cx).cleanable();
+ let mut input = TextInput::new(window, cx).cleanable().h_full();
input.set_text(
"Hello 世界,this is GPUI component, this is a long text.",
window,
@@ -387,7 +387,9 @@ impl Render for InputStory {
.child(self.number_input2.clone()),
),
)
- .child(section("Textarea", cx).child(self.textarea.clone()))
+ .child(
+ section("Textarea", cx).child(div().flex_1().child(self.textarea.clone())),
+ )
.child(
section("Input State", cx)
.child(self.disabled_input.clone())
diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs
index d9383a0c..dbca91ec 100644
--- a/crates/story/src/lib.rs
+++ b/crates/story/src/lib.rs
@@ -182,10 +182,14 @@ impl Render for StoryRoot {
let modal_layer = Root::render_modal_layer(window, cx);
let notification_layer = Root::render_notification_layer(window, cx);
- v_flex()
+ div()
.size_full()
- .child(self.title_bar.clone())
- .child(self.view.clone())
+ .child(
+ v_flex()
+ .size_full()
+ .child(self.title_bar.clone())
+ .child(div().flex_1().overflow_hidden().child(self.view.clone())),
+ )
.children(drawer_layer)
.children(modal_layer)
.child(div().absolute().top_8().children(notification_layer))
diff --git a/crates/ui/src/input/element.rs b/crates/ui/src/input/element.rs
index 7b6a44fd..8d6d929d 100644
--- a/crates/ui/src/input/element.rs
+++ b/crates/ui/src/input/element.rs
@@ -346,11 +346,19 @@ impl Element for TextElement {
let mut style = Style::default();
style.size.width = relative(1.).into();
if self.input.read(cx).is_multi_line() {
- style.size.height = relative(1.).into();
- style.min_size.height = (input.rows.max(1) as f32 * window.line_height()).into();
+ style.flex_grow = 1.0;
+ if let Some(h) = input.height {
+ style.size.height = h.into();
+ style.min_size.height = window.line_height().into();
+ } else {
+ style.size.height = relative(1.).into();
+ style.min_size.height = (input.rows.max(1) as f32 * window.line_height()).into();
+ }
} else {
+ // For single-line inputs, the minimum height should be the line height
style.size.height = window.line_height().into();
};
+
(window.request_layout(style, [], cx), ())
}
diff --git a/crates/ui/src/input/input.rs b/crates/ui/src/input/input.rs
index 18b2b8a7..455806c3 100644
--- a/crates/ui/src/input/input.rs
+++ b/crates/ui/src/input/input.rs
@@ -11,11 +11,12 @@ use unicode_segmentation::*;
use gpui::prelude::FluentBuilder as _;
use gpui::{
- actions, div, point, px, AnyElement, App, AppContext, Bounds, ClickEvent, ClipboardItem,
- Context, Entity, EntityInputHandler, EventEmitter, FocusHandle, Focusable,
- InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton, MouseDownEvent,
- MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render, ScrollHandle,
- ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection, Window, WrappedLine,
+ actions, div, point, px, relative, AnyElement, App, AppContext, Bounds, ClickEvent,
+ ClipboardItem, Context, DefiniteLength, Entity, EntityInputHandler, EventEmitter, FocusHandle,
+ Focusable, InteractiveElement as _, IntoElement, KeyBinding, KeyDownEvent, MouseButton,
+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement as _, Pixels, Point, Rems, Render,
+ ScrollHandle, ScrollWheelEvent, SharedString, Styled as _, Subscription, UTF16Selection,
+ Window, WrappedLine,
};
// TODO:
@@ -223,6 +224,7 @@ pub struct TextInput {
pub(super) cleanable: bool,
pub(super) size: Size,
pub(super) rows: usize,
+ pub(super) height: Option,
pattern: Option,
validate: Option bool + 'static>>,
pub(crate) scroll_handle: ScrollHandle,
@@ -281,6 +283,7 @@ impl TextInput {
prefix: None,
suffix: None,
size: Size::Medium,
+ height: None,
pattern: None,
validate: None,
rows: 2,
@@ -551,6 +554,18 @@ impl TextInput {
self
}
+ /// Set full height of the input (Multi-line only).
+ pub fn h_full(mut self) -> Self {
+ self.height = Some(relative(1.));
+ self
+ }
+
+ /// Set height of the input (Multi-line only).
+ pub fn h(mut self, height: impl Into) -> Self {
+ self.height = Some(height.into());
+ self
+ }
+
/// Set the placeholder text of the input field.
pub fn placeholder(mut self, placeholder: impl Into) -> Self {
self.placeholder = placeholder.into();
@@ -1638,7 +1653,10 @@ impl Render for TextInput {
.input_py(self.size)
.input_h(self.size)
.cursor_text()
- .when(self.multi_line, |this| this.h_auto())
+ .when(self.multi_line, |this| {
+ this.h_auto()
+ .when_some(self.height, |this, height| this.h(height))
+ })
.when(self.appearance, |this| {
this.bg(if self.disabled {
cx.theme().muted
@@ -1659,6 +1677,8 @@ impl Render for TextInput {
.child(
div()
.id("TextElement")
+ .w_full()
+ .when(self.is_multi_line(), |this| this.h_full())
.flex_grow()
.overflow_x_hidden()
.child(TextElement::new(cx.entity().clone())),