show filter-box placeholders + make the search icon optional

The Search tab's Find mode stacked four ClideFilterBoxes (search,
replace, include, exclude) that all looked identical: every box drew the
magnifying glass and the hint was only a semantics label, never visible
text — so they read as four blank search boxes. Render the hint as
placeholder text while empty, and make the leading icon optional (the
replace + glob fields pass icon: null). General win — every filter box
now shows its placeholder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-01 17:20:09 +02:00
co-authored by Claude Opus 4.8
parent a02fc754a8
commit c6a8da1b3a
4 changed files with 56 additions and 14 deletions
+34 -11
View File
@@ -13,6 +13,7 @@ class ClideFilterBox extends StatefulWidget {
this.hint = 'Filter…',
this.debounce = const Duration(milliseconds: 200),
this.onSubmitted,
this.icon = PhosphorIcons.magnifyingGlass,
});
final ValueChanged<String> onChanged;
@@ -20,6 +21,10 @@ class ClideFilterBox extends StatefulWidget {
final Duration debounce;
final ValueChanged<String>? onSubmitted;
/// Leading glyph. Defaults to the search glass; pass null for inputs
/// that aren't searches (e.g. a replace or glob field).
final ClideIconPainter? icon;
@override
State<ClideFilterBox> createState() => _ClideFilterBoxState();
}
@@ -68,18 +73,36 @@ class _ClideFilterBoxState extends State<ClideFilterBox> {
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
child: Row(
children: [
ClideIcon(PhosphorIcons.magnifyingGlass, size: 13, color: tokens.globalTextMuted),
const SizedBox(width: 6),
if (widget.icon != null) ...[
ClideIcon(widget.icon!, size: 13, color: tokens.globalTextMuted),
const SizedBox(width: 6),
],
Expanded(
child: EditableText(
controller: _controller,
focusNode: _focus,
style: TextStyle(fontSize: clideFontCaption, color: tokens.globalForeground),
cursorColor: tokens.globalFocus,
backgroundCursorColor: tokens.globalTextMuted,
maxLines: 1,
onChanged: _onChanged,
onSubmitted: widget.onSubmitted != null ? (v) => widget.onSubmitted!(v) : null,
child: Stack(
alignment: Alignment.centerLeft,
children: [
// Placeholder shown while empty — the hint was only a
// semantics label before, so empty boxes looked blank.
if (!hasText)
IgnorePointer(
child: Text(
widget.hint,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: clideFontCaption, color: tokens.globalTextMuted),
),
),
EditableText(
controller: _controller,
focusNode: _focus,
style: TextStyle(fontSize: clideFontCaption, color: tokens.globalForeground),
cursorColor: tokens.globalFocus,
backgroundCursorColor: tokens.globalTextMuted,
maxLines: 1,
onChanged: _onChanged,
onSubmitted: widget.onSubmitted != null ? (v) => widget.onSubmitted!(v) : null,
),
],
),
),
if (hasText)