Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions crates/moon-ui-components/src/input/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use std::rc::Rc;

use gpui::prelude::FluentBuilder as _;
use gpui::{
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, Entity, Hsla, InteractiveElement as _,
IntoElement, MouseButton, ParentElement as _, Pixels, RenderOnce, StyleRefinement, Styled,
TextAlign, Window, div, px, relative,
AnyElement, App, DefiniteLength, Edges, EdgesRefinement, ElementId, Entity, Hsla,
InteractiveElement as _, IntoElement, MouseButton, ParentElement as _, Pixels, RenderOnce,
StyleRefinement, Styled, TextAlign, Window, div, px, relative,
};

use crate::Sizable;
Expand Down Expand Up @@ -104,6 +104,8 @@ impl MoonInputMetrics {
#[derive(IntoElement)]
pub struct Input {
state: Entity<InputState>,
/// The caller's name for the root element; `None` keys it by the state entity.
id: Option<ElementId>,
style: StyleRefinement,
size: Size,
prefix: Option<AnyElement>,
Expand Down Expand Up @@ -148,6 +150,7 @@ impl Input {
pub fn new(state: &Entity<InputState>) -> Self {
Self {
state: state.clone(),
id: None,
size: Size::default(),
style: StyleRefinement::default(),
prefix: None,
Expand All @@ -166,6 +169,17 @@ impl Input {
}
}

/// Name the input's root element.
///
/// Without it the root is keyed by the state entity, a number that differs between launches,
/// so anything that addresses controls by their element path — an inspector, a scripted
/// test — cannot tell one field from another. The caller's id must be unique among the
/// input's siblings: two siblings with one id share their hover and scroll state.
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
self.id = Some(id.into());
self
}

pub fn prefix(mut self, prefix: impl IntoElement) -> Self {
self.prefix = Some(prefix.into_any_element());
self
Expand Down Expand Up @@ -357,8 +371,11 @@ impl RenderOnce for Input {
&& state.mode.is_single_line();
let has_suffix = suffix.is_some() || state.loading || self.mask_toggle || show_clear_button;

let root_id = self
.id
.unwrap_or_else(|| ("input", self.state.entity_id()).into());
div()
.id(("input", self.state.entity_id()))
.id(root_id)
.flex()
.key_context(crate::input::CONTEXT)
.track_focus(&state.focus_handle.clone())
Expand Down
20 changes: 18 additions & 2 deletions crates/moon-ui-components/src/input/number_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use std::rc::Rc;
use crate::theme::ActiveTheme;
use gpui::Corners;
use gpui::Window;
use gpui::{AnyElement, App, Context, Edges, Entity, EventEmitter, FocusHandle, Focusable};
use gpui::{
AnyElement, App, Context, Edges, ElementId, Entity, EventEmitter, FocusHandle, Focusable,
};
use gpui::{
InteractiveElement, IntoElement, KeyBinding, ParentElement, RenderOnce, SharedString,
StyleRefinement, Styled, TextAlign, actions, prelude::FluentBuilder as _,
Expand All @@ -27,6 +29,8 @@ pub fn init(cx: &mut App) {
#[derive(IntoElement)]
pub struct NumberInput {
state: Entity<InputState>,
/// The caller's name for the root element; `None` keys it by the state entity.
id: Option<ElementId>,
placeholder: SharedString,
size: Size,
prefix: Option<AnyElement>,
Expand All @@ -41,6 +45,7 @@ impl NumberInput {
pub fn new(state: &Entity<InputState>) -> Self {
Self {
state: state.clone(),
id: None,
size: Size::default(),
placeholder: SharedString::default(),
prefix: None,
Expand All @@ -51,6 +56,13 @@ impl NumberInput {
}
}

/// Name the number input's root element, for the same reason as [`Input::id`]: the default is
/// keyed by the state entity, which an element path cannot tell apart between launches.
pub fn id(mut self, id: impl Into<ElementId>) -> Self {
self.id = Some(id.into());
self
}

/// Set the placeholder text of the number input.
pub fn placeholder(mut self, placeholder: impl Into<SharedString>) -> Self {
self.placeholder = placeholder.into();
Expand Down Expand Up @@ -292,8 +304,12 @@ impl RenderOnce for NumberInput {
});
}

let root_id = self
.id
.clone()
.unwrap_or_else(|| ("number-input", self.state.entity_id()).into());
h_flex()
.id(("number-input", self.state.entity_id()))
.id(root_id)
.key_context(CONTEXT)
.on_action(window.listener_for(&self.state, InputState::on_action_increment))
.on_action(window.listener_for(&self.state, InputState::on_action_decrement))
Expand Down
3 changes: 3 additions & 0 deletions crates/moon-ui-components/src/moon/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ impl RenderOnce for MoonInput {
MoonInputSize::Normal
}
});
// The caller's id names the element as well as the state: kept only as a state key, it left
// every field of a form under the input's own internal ids, indistinguishable by path.
let mut input = Input::new(&state)
.id(self.id.clone())
.with_size(size_for(size))
.disabled(self.disabled)
.cleanable(self.cleanable)
Expand Down
2 changes: 2 additions & 0 deletions crates/moon-ui-components/src/moon/number_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,9 @@ impl RenderOnce for MoonNumberInput {
tokens.ui(metrics.pad_x),
tokens.ui(4.0),
);
// The caller's id names the element too, as `MoonInput` does: see `NumberInput::id`.
let mut input = NumberInput::new(&state)
.id(self.id.clone())
.with_size(base_size_for(tier))
.disabled(self.disabled)
.min_w(px(min_w));
Expand Down
20 changes: 10 additions & 10 deletions docs/component-mirror-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@
"local_paths": [
"input"
],
"local_hash": "52d0cc9943a90f3c",
"local_hash": "4c1232652526f5a8",
"local_files": [
{
"path": "input/blink_cursor.rs",
Expand Down Expand Up @@ -511,8 +511,8 @@
},
{
"path": "input/input.rs",
"hash": "4ebe2e7550fade39",
"bytes": 21801
"hash": "3b614fdff94c34a7",
"bytes": 22572
},
{
"path": "input/lsp/code_actions.rs",
Expand Down Expand Up @@ -571,8 +571,8 @@
},
{
"path": "input/number_input.rs",
"hash": "9adbb67bd30a203c",
"bytes": 15815
"hash": "9865642e5e90ed7c",
"bytes": 16385
},
{
"path": "input/otp_input.rs",
Expand Down Expand Up @@ -1512,7 +1512,7 @@
"local_paths": [
"input"
],
"local_hash": "52d0cc9943a90f3c",
"local_hash": "4c1232652526f5a8",
"local_files": [
{
"path": "input/blink_cursor.rs",
Expand Down Expand Up @@ -1586,8 +1586,8 @@
},
{
"path": "input/input.rs",
"hash": "4ebe2e7550fade39",
"bytes": 21801
"hash": "3b614fdff94c34a7",
"bytes": 22572
},
{
"path": "input/lsp/code_actions.rs",
Expand Down Expand Up @@ -1646,8 +1646,8 @@
},
{
"path": "input/number_input.rs",
"hash": "9adbb67bd30a203c",
"bytes": 15815
"hash": "9865642e5e90ed7c",
"bytes": 16385
},
{
"path": "input/otp_input.rs",
Expand Down
Loading