input: Add h, h_full to Input for multi-line mode. (#642)
Close #641 <img width="1712" alt="image" src="https://github.com/user-attachments/assets/9cbe090c-a5d7-4929-98db-5f71349fe559" />
This commit is contained in:
parent
c74791aac8
commit
00e5385878
6 changed files with 82 additions and 29 deletions
|
|
@ -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<Self>) -> 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())),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<TextInput>,
|
||||
}
|
||||
|
||||
const EXAMPLE: &str = include_str!("./markdown.md");
|
||||
|
||||
impl Example {
|
||||
pub fn new(_: &mut Window, _: &mut Context<Self>) -> Self {
|
||||
Self {}
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> 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<Self> {
|
||||
|
|
@ -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(),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ impl InputStory {
|
|||
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> 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())
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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), ())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<gpui::DefiniteLength>,
|
||||
pattern: Option<regex::Regex>,
|
||||
validate: Option<Box<dyn Fn(&str) -> 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<DefiniteLength>) -> Self {
|
||||
self.height = Some(height.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the placeholder text of the input field.
|
||||
pub fn placeholder(mut self, placeholder: impl Into<SharedString>) -> 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())),
|
||||
|
|
|
|||
Loading…
Reference in a new issue