Skip to content

Commit 32c69f0

Browse files
committed
chore: improve UI
1 parent 3437ad8 commit 32c69f0

File tree

3 files changed

+115
-46
lines changed

3 files changed

+115
-46
lines changed

wox.ui.flutter/wox/lib/components/wox_dropdown_button.dart

Lines changed: 83 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,25 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
8686
);
8787
}
8888

89+
Color _getDropdownBackgroundColor() {
90+
final baseDropdownBg = widget.dropdownColor ?? getThemeActiveBackgroundColor();
91+
return baseDropdownBg.withAlpha(255);
92+
}
93+
94+
double _contrastRatio(Color foreground, Color background) {
95+
final foregroundLuminance = foreground.computeLuminance();
96+
final backgroundLuminance = background.computeLuminance();
97+
final bright = foregroundLuminance > backgroundLuminance ? foregroundLuminance : backgroundLuminance;
98+
final dark = foregroundLuminance > backgroundLuminance ? backgroundLuminance : foregroundLuminance;
99+
return (bright + 0.05) / (dark + 0.05);
100+
}
101+
102+
Color _getReadableTextColor(Color background) {
103+
const darkText = Colors.black87;
104+
const lightText = Colors.white;
105+
return _contrastRatio(lightText, background) >= _contrastRatio(darkText, background) ? lightText : darkText;
106+
}
107+
89108
void _markOverlayNeedsBuildSafely() {
90109
if (_overlayEntry == null) {
91110
return;
@@ -237,7 +256,14 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
237256

238257
void _showFilterableMenu() {
239258
final activeTextColor = getThemeActiveTextColor();
240-
final dropdownBg = widget.dropdownColor ?? getThemeActiveBackgroundColor().withAlpha(255);
259+
final dropdownBg = _getDropdownBackgroundColor();
260+
final searchBg =
261+
dropdownBg.computeLuminance() > 0.45
262+
? Color.alphaBlend(Colors.black.withValues(alpha: 0.08), dropdownBg)
263+
: Color.alphaBlend(Colors.white.withValues(alpha: 0.08), dropdownBg);
264+
final searchTextColor = _getReadableTextColor(searchBg);
265+
final searchHintColor = searchTextColor.withValues(alpha: 0.55);
266+
final searchDividerColor = searchTextColor.withValues(alpha: 0.20);
241267
final borderColor = getThemeSubTextColor();
242268

243269
final RenderBox renderBox = context.findRenderObject() as RenderBox;
@@ -261,6 +287,7 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
261287
borderRadius: BorderRadius.circular(4),
262288
color: dropdownBg,
263289
child: Container(
290+
clipBehavior: Clip.antiAlias,
264291
constraints: BoxConstraints(maxHeight: widget.menuMaxHeight ?? 300),
265292
decoration: BoxDecoration(border: Border.all(color: borderColor), borderRadius: BorderRadius.circular(4)),
266293
child: Column(
@@ -269,47 +296,65 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
269296
// Filter text field
270297
Container(
271298
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
272-
decoration: BoxDecoration(border: Border(bottom: BorderSide(color: borderColor))),
273-
child: TextField(
274-
controller: _filterController,
275-
focusNode: _filterFocusNode,
276-
autofocus: true,
277-
style: TextStyle(color: activeTextColor, fontSize: widget.fontSize).useSystemChineseFont(),
278-
decoration: InputDecoration(
279-
hintText: 'Filter...',
280-
hintStyle: TextStyle(color: activeTextColor.withValues(alpha: 0.5), fontSize: widget.fontSize).useSystemChineseFont(),
281-
border: InputBorder.none,
282-
isDense: true,
283-
contentPadding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
284-
prefixIcon: Icon(Icons.search, size: 16, color: activeTextColor.withValues(alpha: 0.7)),
299+
decoration: BoxDecoration(
300+
color: searchBg,
301+
border: Border(bottom: BorderSide(color: searchDividerColor)),
302+
borderRadius: const BorderRadius.vertical(top: Radius.circular(4)),
303+
),
304+
child: Focus(
305+
onKeyEvent: (node, event) {
306+
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) {
307+
_removeOverlay();
308+
return KeyEventResult.handled;
309+
}
310+
return KeyEventResult.ignored;
311+
},
312+
child: TextField(
313+
controller: _filterController,
314+
focusNode: _filterFocusNode,
315+
autofocus: true,
316+
textAlignVertical: TextAlignVertical.center,
317+
style: TextStyle(color: searchTextColor, fontSize: widget.fontSize).useSystemChineseFont(),
318+
decoration: InputDecoration(
319+
hintText: 'Filter...',
320+
hintStyle: TextStyle(color: searchHintColor, fontSize: widget.fontSize).useSystemChineseFont(),
321+
border: InputBorder.none,
322+
isDense: true,
323+
contentPadding: const EdgeInsets.symmetric(vertical: 8),
324+
prefixIcon: Padding(padding: const EdgeInsets.only(left: 4, right: 6), child: Icon(Icons.search, size: 16, color: searchHintColor)),
325+
prefixIconConstraints: const BoxConstraints(minWidth: 22, minHeight: 22),
326+
),
327+
onChanged: _filterItems,
285328
),
286-
onChanged: _filterItems,
287329
),
288330
),
289331
// Filtered items list
290332
Flexible(
291-
child: ListView.builder(
292-
shrinkWrap: true,
293-
padding: EdgeInsets.zero,
294-
itemCount: _filteredItems.length,
295-
itemBuilder: (context, index) {
296-
final item = _filteredItems[index];
297-
final isSelected = item.value == widget.value;
298-
return _buildNoRippleInkWell(
299-
onTap: () {
300-
widget.onChanged?.call(item.value);
301-
_removeOverlay();
302-
},
303-
child: Container(
304-
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
305-
color: isSelected ? activeTextColor.withValues(alpha: 0.1) : null,
306-
child: DefaultTextStyle(
307-
style: TextStyle(color: activeTextColor, fontSize: widget.fontSize).useSystemChineseFont(),
308-
child: _buildDropdownMenuItem(item, activeTextColor),
333+
child: Container(
334+
color: dropdownBg,
335+
child: ListView.builder(
336+
shrinkWrap: true,
337+
padding: EdgeInsets.zero,
338+
itemCount: _filteredItems.length,
339+
itemBuilder: (context, index) {
340+
final item = _filteredItems[index];
341+
final isSelected = item.value == widget.value;
342+
return _buildNoRippleInkWell(
343+
onTap: () {
344+
widget.onChanged?.call(item.value);
345+
_removeOverlay();
346+
},
347+
child: Container(
348+
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
349+
color: isSelected ? activeTextColor.withValues(alpha: 0.1) : null,
350+
child: DefaultTextStyle(
351+
style: TextStyle(color: activeTextColor, fontSize: widget.fontSize).useSystemChineseFont(),
352+
child: _buildDropdownMenuItem(item, activeTextColor),
353+
),
309354
),
310-
),
311-
);
312-
},
355+
);
356+
},
357+
),
313358
),
314359
),
315360
],
@@ -357,7 +402,7 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
357402

358403
void _showMultiSelectMenu() {
359404
final activeTextColor = getThemeActiveTextColor();
360-
final dropdownBg = widget.dropdownColor ?? getThemeActiveBackgroundColor().withAlpha(255);
405+
final dropdownBg = _getDropdownBackgroundColor();
361406
final borderColor = getThemeSubTextColor();
362407

363408
final RenderBox renderBox = context.findRenderObject() as RenderBox;
@@ -493,7 +538,7 @@ class _WoxDropdownButtonState<T> extends State<WoxDropdownButton<T>> {
493538
Widget build(BuildContext context) {
494539
final textColor = getThemeTextColor();
495540
final activeTextColor = getThemeActiveTextColor();
496-
final dropdownBg = widget.dropdownColor ?? getThemeActiveBackgroundColor().withAlpha(255);
541+
final dropdownBg = _getDropdownBackgroundColor();
497542
final borderColor = getThemeSubTextColor();
498543

499544
if (widget.multiSelect) {

wox.ui.flutter/wox/lib/components/wox_setting_form_field.dart

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:wox/components/wox_tooltip.dart';
23
import 'package:wox/utils/colors.dart';
34

45
class WoxSettingFormField extends StatelessWidget {
@@ -32,13 +33,7 @@ class WoxSettingFormField extends StatelessWidget {
3233
Row(
3334
crossAxisAlignment: rowCrossAxisAlignment,
3435
children: [
35-
Padding(
36-
padding: EdgeInsets.only(right: labelGap),
37-
child: SizedBox(
38-
width: labelWidth,
39-
child: Text(label, textAlign: TextAlign.right, style: TextStyle(color: getThemeTextColor(), fontSize: 13), overflow: TextOverflow.ellipsis),
40-
),
41-
),
36+
Padding(padding: EdgeInsets.only(right: labelGap), child: SizedBox(width: labelWidth, child: _SettingLabelWithTooltip(label: label))),
4237
Flexible(child: Align(alignment: Alignment.centerLeft, child: child)),
4338
],
4439
),
@@ -52,3 +47,32 @@ class WoxSettingFormField extends StatelessWidget {
5247
);
5348
}
5449
}
50+
51+
class _SettingLabelWithTooltip extends StatelessWidget {
52+
final String label;
53+
54+
const _SettingLabelWithTooltip({required this.label});
55+
56+
@override
57+
Widget build(BuildContext context) {
58+
final textStyle = TextStyle(color: getThemeTextColor(), fontSize: 13);
59+
final textWidget = Text(label, textAlign: TextAlign.right, style: textStyle, maxLines: 1, overflow: TextOverflow.ellipsis);
60+
61+
return LayoutBuilder(
62+
builder: (context, constraints) {
63+
final maxWidth = constraints.maxWidth;
64+
if (!maxWidth.isFinite || maxWidth <= 0) {
65+
return textWidget;
66+
}
67+
68+
final textPainter = TextPainter(text: TextSpan(text: label, style: textStyle), maxLines: 1, textDirection: Directionality.of(context))..layout(maxWidth: maxWidth);
69+
70+
if (!textPainter.didExceedMaxLines) {
71+
return textWidget;
72+
}
73+
74+
return WoxTooltip(message: label, child: textWidget);
75+
},
76+
);
77+
}
78+
}

wox.ui.flutter/wox/lib/utils/windows/windows_window_manager.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class WindowsWindowManager extends BaseWindowManager {
5555
case 'log':
5656
// Log messages from native code
5757
final message = call.arguments as String;
58-
Logger.instance.info(const UuidV4().generate(), " [NATIVE] $message");
58+
Logger.instance.info(const UuidV4().generate(), "[NATIVE] $message");
5959
break;
6060
case 'onKeyboardEvent':
6161
// Handle keyboard events from Windows message loop

0 commit comments

Comments
 (0)