Skip to main content

gpui_component/input/
content_type.rs

1use gpui::Window;
2
3/// Semantic content type for an [`Input`](super::Input).
4///
5/// These variants mirror Swift's text content types.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum InputContentType {
8    /// A person's full name.
9    Name,
10    /// A name prefix, such as Mr. or Dr.
11    NamePrefix,
12    /// A person's given name.
13    GivenName,
14    /// A person's middle name.
15    MiddleName,
16    /// A person's family name.
17    FamilyName,
18    /// A name suffix, such as Jr. or PhD.
19    NameSuffix,
20    /// A nickname.
21    Nickname,
22    /// A job title.
23    JobTitle,
24    /// An organization or company name.
25    OrganizationName,
26    /// A location name.
27    Location,
28    /// A full street address.
29    FullStreetAddress,
30    /// The first line of a street address.
31    StreetAddressLine1,
32    /// The second line of a street address.
33    StreetAddressLine2,
34    /// A city or locality.
35    AddressCity,
36    /// A state, province, or region.
37    AddressState,
38    /// A combined city and state.
39    AddressCityAndState,
40    /// A sublocality, district, or neighborhood.
41    Sublocality,
42    /// A country name.
43    CountryName,
44    /// A postal or ZIP code.
45    PostalCode,
46    /// A telephone number.
47    TelephoneNumber,
48    /// An email address.
49    EmailAddress,
50    /// A URL.
51    Url,
52    /// A credit card number.
53    CreditCardNumber,
54    /// The full name on a credit card.
55    CreditCardName,
56    /// The given name on a credit card.
57    CreditCardGivenName,
58    /// The middle name on a credit card.
59    CreditCardMiddleName,
60    /// The family name on a credit card.
61    CreditCardFamilyName,
62    /// The security code on a credit card.
63    CreditCardSecurityCode,
64    /// A credit card expiration date.
65    CreditCardExpiration,
66    /// A credit card expiration month.
67    CreditCardExpirationMonth,
68    /// A credit card expiration year.
69    CreditCardExpirationYear,
70    /// A credit card type.
71    CreditCardType,
72    /// A username or account identifier.
73    Username,
74    /// The password for the account identified by the username field.
75    Password,
76    /// A new password, such as during sign up or password reset.
77    NewPassword,
78    /// A one-time verification code.
79    OneTimeCode,
80    /// A parcel shipment tracking number.
81    ShipmentTrackingNumber,
82    /// An airline flight number.
83    FlightNumber,
84    /// A date, time, or duration.
85    DateTime,
86    /// A birthdate.
87    Birthdate,
88    /// A birthdate day.
89    BirthdateDay,
90    /// A birthdate month.
91    BirthdateMonth,
92    /// A birthdate year.
93    BirthdateYear,
94    /// An eSIM EID.
95    CellularEid,
96    /// A cellular IMEI.
97    CellularImei,
98}
99
100impl InputContentType {
101    #[cfg(target_os = "macos")]
102    pub(crate) const fn ns_text_content_type(self) -> Option<&'static str> {
103        match self {
104            Self::Name => Some("name"),
105            Self::NamePrefix => Some("honorific-prefix"),
106            Self::GivenName => Some("given-name"),
107            Self::MiddleName => Some("additional-name"),
108            Self::FamilyName => Some("family-name"),
109            Self::NameSuffix => Some("honorific-suffix"),
110            Self::Nickname => Some("nickname"),
111            Self::JobTitle => Some("organization-title"),
112            Self::OrganizationName => Some("organization"),
113            Self::Location => Some("location"),
114            Self::FullStreetAddress => Some("street-address"),
115            Self::StreetAddressLine1 => Some("address-line1"),
116            Self::StreetAddressLine2 => Some("address-line2"),
117            Self::AddressCity => Some("address-level2"),
118            Self::AddressState => Some("address-level1"),
119            Self::AddressCityAndState => Some("address-level1+2"),
120            Self::Sublocality => Some("address-level3"),
121            Self::CountryName => Some("country-name"),
122            Self::PostalCode => Some("postal-code"),
123            Self::TelephoneNumber => Some("tel"),
124            Self::EmailAddress => Some("email"),
125            Self::Url => Some("url"),
126            Self::CreditCardNumber => Some("cc-number"),
127            Self::CreditCardName => Some("cc-name"),
128            Self::CreditCardGivenName => Some("cc-given-name"),
129            Self::CreditCardMiddleName => Some("cc-additional-name"),
130            Self::CreditCardFamilyName => Some("cc-family-name"),
131            Self::CreditCardSecurityCode => Some("cc-csc"),
132            Self::CreditCardExpiration => Some("cc-exp"),
133            Self::CreditCardExpirationMonth => Some("cc-exp-month"),
134            Self::CreditCardExpirationYear => Some("cc-exp-year"),
135            Self::CreditCardType => Some("cc-type"),
136            Self::Username => Some("username"),
137            Self::Password => Some("password"),
138            Self::NewPassword => Some("new-password"),
139            Self::OneTimeCode => Some("one-time-code"),
140            Self::ShipmentTrackingNumber => Some("shipment-tracking-number"),
141            Self::FlightNumber => Some("flight-number"),
142            Self::DateTime => Some("date-time"),
143            Self::Birthdate => Some("bday"),
144            Self::BirthdateDay => Some("bday-day"),
145            Self::BirthdateMonth => Some("bday-month"),
146            Self::BirthdateYear => Some("bday-year"),
147            Self::CellularEid | Self::CellularImei => None,
148        }
149    }
150}
151
152/// Tells the OS what the focused input is used for, to let the autofill offer
153/// the matched value.
154///
155/// The `editable` is false when the input is `disabled` or `readonly`, in that
156/// case the autofill has nothing to fill in, so the content type is not synced.
157pub(super) fn sync_native_content_type(
158    window: &mut Window,
159    content_type: Option<InputContentType>,
160    editable: bool,
161) {
162    if !editable {
163        return;
164    }
165
166    #[cfg(all(target_os = "macos", not(test)))]
167    gpui_base::input::set_text_content_type(
168        window,
169        content_type.and_then(InputContentType::ns_text_content_type),
170    );
171
172    #[cfg(any(not(target_os = "macos"), test))]
173    let _ = (window, content_type);
174}
175
176#[cfg(all(test, target_os = "macos"))]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn content_type_maps_to_ns_text_content_type_values() {
182        let content_types = [
183            (InputContentType::Name, Some("name")),
184            (InputContentType::NamePrefix, Some("honorific-prefix")),
185            (InputContentType::GivenName, Some("given-name")),
186            (InputContentType::MiddleName, Some("additional-name")),
187            (InputContentType::FamilyName, Some("family-name")),
188            (InputContentType::NameSuffix, Some("honorific-suffix")),
189            (InputContentType::Nickname, Some("nickname")),
190            (InputContentType::JobTitle, Some("organization-title")),
191            (InputContentType::OrganizationName, Some("organization")),
192            (InputContentType::Location, Some("location")),
193            (InputContentType::FullStreetAddress, Some("street-address")),
194            (InputContentType::StreetAddressLine1, Some("address-line1")),
195            (InputContentType::StreetAddressLine2, Some("address-line2")),
196            (InputContentType::AddressCity, Some("address-level2")),
197            (InputContentType::AddressState, Some("address-level1")),
198            (
199                InputContentType::AddressCityAndState,
200                Some("address-level1+2"),
201            ),
202            (InputContentType::Sublocality, Some("address-level3")),
203            (InputContentType::CountryName, Some("country-name")),
204            (InputContentType::PostalCode, Some("postal-code")),
205            (InputContentType::TelephoneNumber, Some("tel")),
206            (InputContentType::EmailAddress, Some("email")),
207            (InputContentType::Url, Some("url")),
208            (InputContentType::CreditCardNumber, Some("cc-number")),
209            (InputContentType::CreditCardName, Some("cc-name")),
210            (InputContentType::CreditCardGivenName, Some("cc-given-name")),
211            (
212                InputContentType::CreditCardMiddleName,
213                Some("cc-additional-name"),
214            ),
215            (
216                InputContentType::CreditCardFamilyName,
217                Some("cc-family-name"),
218            ),
219            (InputContentType::CreditCardSecurityCode, Some("cc-csc")),
220            (InputContentType::CreditCardExpiration, Some("cc-exp")),
221            (
222                InputContentType::CreditCardExpirationMonth,
223                Some("cc-exp-month"),
224            ),
225            (
226                InputContentType::CreditCardExpirationYear,
227                Some("cc-exp-year"),
228            ),
229            (InputContentType::CreditCardType, Some("cc-type")),
230            (InputContentType::Username, Some("username")),
231            (InputContentType::Password, Some("password")),
232            (InputContentType::NewPassword, Some("new-password")),
233            (InputContentType::OneTimeCode, Some("one-time-code")),
234            (
235                InputContentType::ShipmentTrackingNumber,
236                Some("shipment-tracking-number"),
237            ),
238            (InputContentType::FlightNumber, Some("flight-number")),
239            (InputContentType::DateTime, Some("date-time")),
240            (InputContentType::Birthdate, Some("bday")),
241            (InputContentType::BirthdateDay, Some("bday-day")),
242            (InputContentType::BirthdateMonth, Some("bday-month")),
243            (InputContentType::BirthdateYear, Some("bday-year")),
244            (InputContentType::CellularEid, None),
245            (InputContentType::CellularImei, None),
246        ];
247
248        for (content_type, native_value) in content_types {
249            assert_eq!(content_type.ns_text_content_type(), native_value);
250        }
251    }
252}