Skip to content

Commit d7a9b9d

Browse files
authored
feat: Improve Markdown selection when selectable=True (#5773)
* Improve markdown selection * Fix #5748 | Add docs for `KeyboardType` and `TextCapitalization` enums, refactor `parseTextInputType`
1 parent e7f52ed commit d7a9b9d

File tree

3 files changed

+178
-54
lines changed

3 files changed

+178
-54
lines changed

packages/flet/lib/src/controls/markdown.dart

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,37 @@ import 'highlight_view.dart';
1717
class MarkdownControl extends StatelessWidget {
1818
final Control control;
1919

20-
static const String svgTag = " xmlns=\"http://www.w3.org/2000/svg\"";
21-
2220
const MarkdownControl({super.key, required this.control});
2321

2422
@override
2523
Widget build(BuildContext context) {
2624
debugPrint("Markdown build: ${control.id}");
2725

26+
final theme = Theme.of(context);
2827
var value = control.getString("value", "")!;
29-
md.ExtensionSet extensionSet =
28+
var extensionSet =
3029
control.getMarkdownExtensionSet("extension_set", md.ExtensionSet.none)!;
3130

3231
var autoFollowLinks = control.getBool("auto_follow_links", false)!;
3332
var autoFollowLinksTarget = control.getString("auto_follow_links_target");
33+
var selectable = control.getBool("selectable", false)!;
3434

35-
bool selectable = control.getBool("selectable", false)!;
36-
var codeStyleSheet =
37-
control.getMarkdownStyleSheet("code_style_sheet", context) ??
38-
MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith(
39-
code: Theme.of(context)
40-
.textTheme
41-
.bodyMedium!
42-
.copyWith(fontFamily: "monospace"));
35+
var codeStyleSheet = control.getMarkdownStyleSheet(
36+
"code_style_sheet", context) ??
37+
MarkdownStyleSheet.fromTheme(theme).copyWith(
38+
code:
39+
theme.textTheme.bodyMedium!.copyWith(fontFamily: "monospace"));
4340
var mdStyleSheet = control.getMarkdownStyleSheet("md_style_sheet", context);
44-
var codeTheme =
45-
control.getMarkdownCodeTheme("code_theme", Theme.of(context));
41+
var codeTheme = control.getMarkdownCodeTheme("code_theme", theme);
42+
4643
Widget markdown = MarkdownBody(
4744
data: value,
48-
selectable: selectable,
4945
imageDirectory: control.backend.assetsDir != ""
5046
? control.backend.assetsDir
5147
: getBaseUri(control.backend.pageUri).toString(),
5248
extensionSet: extensionSet,
5349
builders: {
54-
'code': CodeElementBuilder(codeTheme, codeStyleSheet, selectable),
50+
'code': CodeElementBuilder(codeTheme, codeStyleSheet),
5551
},
5652
styleSheet: mdStyleSheet,
5753
imageBuilder: (Uri uri, String? title, String? alt) {
@@ -100,16 +96,18 @@ class MarkdownControl extends StatelessWidget {
10096
control.triggerEvent("tap_link", href);
10197
});
10298

103-
return LayoutControl(control: control, child: markdown);
99+
return LayoutControl(
100+
control: control,
101+
child: selectable ? SelectionArea(child: markdown) : markdown,
102+
);
104103
}
105104
}
106105

107106
class CodeElementBuilder extends MarkdownElementBuilder {
108107
final Map<String, TextStyle> codeTheme;
109108
final MarkdownStyleSheet mdStyleSheet;
110-
final bool selectable;
111109

112-
CodeElementBuilder(this.codeTheme, this.mdStyleSheet, this.selectable);
110+
CodeElementBuilder(this.codeTheme, this.mdStyleSheet);
113111

114112
@override
115113
Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) {
@@ -132,8 +130,7 @@ class CodeElementBuilder extends MarkdownElementBuilder {
132130
// The original code to be highlighted
133131
element.textContent.substring(0, element.textContent.length - 1),
134132

135-
// Specify language
136-
// It is recommended to give it a value for performance
133+
// It is recommended to give a language for performance
137134
language: language,
138135

139136
// Specify highlight theme
@@ -142,8 +139,6 @@ class CodeElementBuilder extends MarkdownElementBuilder {
142139
padding: mdStyleSheet.codeblockPadding,
143140
decoration: mdStyleSheet.codeblockDecoration,
144141
textStyle: mdStyleSheet.code,
145-
146-
selectable: selectable,
147142
),
148143
);
149144
});

packages/flet/lib/src/utils/form_field.dart

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,22 @@ FormFieldInputBorder? parseFormFieldInputBorder(String? value,
2424

2525
TextInputType? parseTextInputType(String? value,
2626
[TextInputType? defaultValue]) {
27-
switch (value?.toLowerCase()) {
28-
case "datetime":
29-
return TextInputType.datetime;
30-
case "email":
31-
return TextInputType.emailAddress;
32-
case "multiline":
33-
return TextInputType.multiline;
34-
case "name":
35-
return TextInputType.name;
36-
case "none":
37-
return TextInputType.none;
38-
case "number":
39-
return TextInputType.number;
40-
case "phone":
41-
return TextInputType.phone;
42-
case "streetaddress":
43-
return TextInputType.streetAddress;
44-
case "text":
45-
return TextInputType.text;
46-
case "url":
47-
return TextInputType.url;
48-
case "visiblepassword":
49-
return TextInputType.visiblePassword;
50-
default:
51-
return defaultValue;
52-
}
27+
const typeMap = {
28+
"datetime": TextInputType.datetime,
29+
"email": TextInputType.emailAddress,
30+
"multiline": TextInputType.multiline,
31+
"name": TextInputType.name,
32+
"none": TextInputType.none,
33+
"number": TextInputType.number,
34+
"phone": TextInputType.phone,
35+
"streetaddress": TextInputType.streetAddress,
36+
"text": TextInputType.text,
37+
"url": TextInputType.url,
38+
"visiblepassword": TextInputType.visiblePassword,
39+
"websearch": TextInputType.webSearch,
40+
"twitter": TextInputType.twitter,
41+
};
42+
return typeMap[value?.toLowerCase()] ?? defaultValue;
5343
}
5444

5545
InputDecoration buildInputDecoration(
@@ -86,7 +76,7 @@ InputDecoration buildInputDecoration(
8676
?.replaceAll("{value_length}", valueLength.toString())
8777
.replaceAll("{max_length}", maxLength?.toString() ?? "None")
8878
.replaceAll("{symbols_left}",
89-
"${maxLength == null ? 'None' : (maxLength - (valueLength ?? 0))}");
79+
"${maxLength == null ? 'None' : (maxLength - (valueLength ?? 0))}");
9080
}
9181

9282
// error

sdk/python/packages/flet/src/flet/controls/material/textfield.py

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,29 +30,168 @@
3030

3131

3232
class KeyboardType(Enum):
33+
"""
34+
The type of information for which to optimize the text input control.
35+
36+
On Android, behavior may vary across device and keyboard provider.
37+
"""
38+
3339
NONE = "none"
40+
"""
41+
Prevents the OS from showing the on-screen virtual keyboard.
42+
"""
43+
3444
TEXT = "text"
45+
"""
46+
Optimized for textual information.
47+
48+
Requests the default platform keyboard.
49+
"""
50+
3551
MULTILINE = "multiline"
52+
"""
53+
Optimized for multiline textual information.
54+
55+
Requests the default platform keyboard, but accepts newlines when the
56+
enter key is pressed. This is the input type used for all multiline text
57+
fields.
58+
"""
59+
3660
NUMBER = "number"
61+
"""
62+
Optimized for unsigned numerical information without a decimal point.
63+
64+
Requests a default keyboard with ready access to the number keys.
65+
"""
66+
3767
PHONE = "phone"
68+
"""
69+
Optimized for telephone numbers.
70+
71+
Requests a keyboard with ready access to the number keys, `"*"`, and `"#"`.
72+
"""
73+
3874
DATETIME = "datetime"
75+
"""
76+
Optimized for date and time information.
77+
78+
- On iOS, requests the default keyboard.
79+
- On Android, requests a keyboard with ready
80+
access to the number keys, `":"`, and `"-"`.
81+
"""
82+
3983
EMAIL = "email"
84+
"""
85+
Optimized for email addresses.
86+
87+
Requests a keyboard with ready access to the `"@"` and `"."` keys.
88+
"""
89+
4090
URL = "url"
91+
"""
92+
Optimized for URLs.
93+
94+
Requests a keyboard with ready access to the `"/"` and `"."` keys.
95+
"""
96+
4197
VISIBLE_PASSWORD = "visiblePassword"
98+
"""
99+
Optimized for passwords that are visible to the user.
100+
101+
Requests a keyboard with ready access to both letters and numbers.
102+
"""
103+
42104
NAME = "name"
105+
"""
106+
Optimized for a person's name.
107+
108+
- On iOS, requests the [UIKeyboardType.namePhonePad](https://developer.apple.com/documentation/uikit/uikeyboardtype/namephonepad)
109+
keyboard, a keyboard optimized for entering a person’s name or phone number.
110+
Does not support auto-capitalization.
111+
- On Android, requests a keyboard optimized for
112+
[TYPE_TEXT_VARIATION_PERSON_NAME](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_PERSON_NAME).
113+
""" # noqa: E501
114+
43115
STREET_ADDRESS = "streetAddress"
116+
"""
117+
Optimized for postal mailing addresses.
118+
119+
- On iOS, requests the default keyboard.
120+
- On Android, requests a keyboard optimized for
121+
[TYPE_TEXT_VARIATION_POSTAL_ADDRESS](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_POSTAL_ADDRESS).
122+
""" # noqa: E501
123+
124+
WEB_SEARCH = "webSearch"
125+
"""
126+
Optimized for web searches.
127+
128+
Requests a keyboard that includes keys useful for web searches as well as URLs.
129+
130+
- On iOS, requests a default keyboard with ready access to the `"."` key.
131+
In contrast to [`URL`][(c).], a space bar is available.
132+
- On Android this is remapped to the [`URL`][(c).] keyboard type as it always
133+
shows a space bar.
134+
"""
135+
136+
TWITTER = "twitter"
137+
"""
138+
Optimized for social media.
139+
140+
Requests a keyboard that includes keys useful for handles and tags.
141+
142+
- On iOS, requests a default keyboard with ready access to the `"@"` and `"#"` keys.
143+
- On Android this is remapped to the [`EMAIL`][(c).] keyboard type as it
144+
always shows the `"@"` key.
145+
"""
44146

45147

46148
class TextCapitalization(Enum):
149+
"""
150+
Configures how the platform keyboard will select an uppercase or
151+
lowercase keyboard.
152+
153+
Only supports text keyboards, other keyboard types will ignore this
154+
configuration. Capitalization is locale-aware.
155+
"""
156+
47157
CHARACTERS = "characters"
158+
"""
159+
Uppercase keyboard for each character.
160+
161+
Info:
162+
Corresponds to `InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS` on Android, and
163+
`UITextAutocapitalizationTypeAllCharacters` on iOS.
164+
"""
165+
48166
WORDS = "words"
167+
"""
168+
Uppercase keyboard for the first letter of each word.
169+
170+
Info:
171+
Corresponds to `InputType.TYPE_TEXT_FLAG_CAP_WORDS` on Android, and
172+
`UITextAutocapitalizationTypeWords` on iOS.
173+
"""
174+
49175
SENTENCES = "sentences"
176+
"""
177+
Uppercase keyboard for the first letter of each sentence.
178+
179+
Info:
180+
Corresponds to `InputType.TYPE_TEXT_FLAG_CAP_SENTENCES` on Android, and
181+
`UITextAutocapitalizationTypeSentences` on iOS.
182+
"""
183+
184+
NONE = "none"
185+
"""
186+
Lowercase keyboard.
187+
"""
50188

51189

52190
@dataclass
53191
class InputFilter:
54192
"""
55-
`InputFilter` class.
193+
An input filter that uses a regular expression to allow or deny/block certain
194+
patterns in the input.
56195
"""
57196

58197
regex_string: str
@@ -79,7 +218,7 @@ class InputFilter:
79218
"""
80219
Whether this regular expression matches multiple lines.
81220
82-
If the regexp does match multiple lines, the "^" and "$" characters match the
221+
If the regexp does match multiple lines, the `"^"` and `"$"` characters match the
83222
beginning and end of lines. If not, the characters match the beginning and end of
84223
the input.
85224
"""
@@ -100,10 +239,10 @@ class InputFilter:
100239

101240
dot_all: bool = False
102241
"""
103-
Whether "." in this regular expression matches line terminators.
242+
Whether `"."` in this regular expression matches line terminators.
104243
105-
When false, the "." character matches a single character, unless that character
106-
terminates a line. When true, then the "." character will match any single
244+
When false, the `"."` character matches a single character, unless that character
245+
terminates a line. When true, then the `"."` character will match any single
107246
character including line terminators.
108247
109248
This feature is distinct from `multiline`. They affect the behavior of different

0 commit comments

Comments
 (0)