From 8e5adf7c5c3d181dddcb4c43891e173be79d1577 Mon Sep 17 00:00:00 2001 From: qianlifeng Date: Mon, 26 May 2025 09:22:22 +0800 Subject: [PATCH] feat(ui): enhance input method switching on query box focus * Updated the input method switch logic to trigger when the query box gains focus. * Improved user experience by providing clearer instructions in the UI language files. * Added a new API endpoint for handling query box focus events. * Implemented caching for image rendering to reduce flickering during refreshes. --- wox.core/resource/lang/en_US.json | 2 +- wox.core/resource/lang/pt_BR.json | 2 +- wox.core/resource/lang/ru_RU.json | 2 +- wox.core/resource/lang/zh_CN.json | 2 +- wox.core/ui/manager.go | 6 +- wox.core/ui/router.go | 15 +++-- wox.ui.flutter/wox/lib/api/wox_api.dart | 4 ++ .../wox/lib/components/wox_image_view.dart | 57 +++++++++++++++---- .../wox/lib/components/wox_list_view.dart | 3 - .../controllers/wox_launcher_controller.dart | 9 ++- 10 files changed, 77 insertions(+), 25 deletions(-) diff --git a/wox.core/resource/lang/en_US.json b/wox.core/resource/lang/en_US.json index edc3d649..d943954d 100644 --- a/wox.core/resource/lang/en_US.json +++ b/wox.core/resource/lang/en_US.json @@ -23,7 +23,7 @@ "ui_show_position_active_screen": "Active screen", "ui_show_position_last_location": "Last location", "ui_switch_input_method_abc": "Switch to ABC", - "ui_switch_input_method_abc_tips": "When selected, the input method will be switched to english", + "ui_switch_input_method_abc_tips": "When selected, the input method will be switched to english when the query box gains focus", "ui_lang": "Language", "ui_query_hotkeys": "Query Hotkeys", "ui_query_shortcuts": "Query Shortcuts", diff --git a/wox.core/resource/lang/pt_BR.json b/wox.core/resource/lang/pt_BR.json index 1744a986..14b7ef65 100644 --- a/wox.core/resource/lang/pt_BR.json +++ b/wox.core/resource/lang/pt_BR.json @@ -21,7 +21,7 @@ "ui_show_position_active_screen": "Tela ativa", "ui_show_position_last_location": "Última posição", "ui_switch_input_method_abc": "Alternar para ABC", - "ui_switch_input_method_abc_tips": "Quando selecionado, o método de entrada será alterado para o inglês", + "ui_switch_input_method_abc_tips": "Quando selecionado, o método de entrada será alterado para o inglês quando a caixa de consulta receber o foco", "ui_lang": "Idioma", "ui_query_hotkeys": "Teclas de atalho para consulta", "ui_query_shortcuts": "Atalhos de consulta", diff --git a/wox.core/resource/lang/ru_RU.json b/wox.core/resource/lang/ru_RU.json index 0bf32155..d3ae37c8 100644 --- a/wox.core/resource/lang/ru_RU.json +++ b/wox.core/resource/lang/ru_RU.json @@ -21,7 +21,7 @@ "ui_show_position_active_screen": "Активный экран", "ui_show_position_last_location": "Последнее положение", "ui_switch_input_method_abc": "Переключить на ABC", - "ui_switch_input_method_abc_tips": "При выборе метод ввода будет переключен на английский", + "ui_switch_input_method_abc_tips": "При выборе метод ввода будет переключен на английский при получении фокуса полем запроса", "ui_lang": "Язык", "ui_query_hotkeys": "Горячие клавиши запроса", "ui_query_shortcuts": "Ярлыки запросов", diff --git a/wox.core/resource/lang/zh_CN.json b/wox.core/resource/lang/zh_CN.json index 6cfd6dca..692dd24d 100644 --- a/wox.core/resource/lang/zh_CN.json +++ b/wox.core/resource/lang/zh_CN.json @@ -23,7 +23,7 @@ "ui_show_position_active_screen": "活动屏幕", "ui_show_position_last_location": "上次位置", "ui_switch_input_method_abc": "切换输入法", - "ui_switch_input_method_abc_tips": "选中后,输入法将切换到英文", + "ui_switch_input_method_abc_tips": "选中后,查询框获得焦点时输入法将切换到英文", "ui_lang": "语言", "ui_query_hotkeys": "查询快捷", "ui_query_shortcuts": "查询缩写", diff --git a/wox.core/ui/manager.go b/wox.core/ui/manager.go index 79607ba0..cf428703 100644 --- a/wox.core/ui/manager.go +++ b/wox.core/ui/manager.go @@ -419,9 +419,13 @@ func (m *Manager) PostUIReady(ctx context.Context) { } func (m *Manager) PostOnShow(ctx context.Context) { + //no-op +} + +func (m *Manager) PostOnQueryBoxFocus(ctx context.Context) { woxSetting := setting.GetSettingManager().GetWoxSetting(ctx) if woxSetting.SwitchInputMethodABC { - util.GetLogger().Info(ctx, "switch input method to ABC") + util.GetLogger().Info(ctx, "switch input method to ABC on query box focus") switchErr := ime.SwitchInputMethodABC() if switchErr != nil { logger.Error(ctx, fmt.Sprintf("failed to switch input method to ABC: %s", switchErr.Error())) diff --git a/wox.core/ui/router.go b/wox.core/ui/router.go index f40c6405..86e2bcc6 100644 --- a/wox.core/ui/router.go +++ b/wox.core/ui/router.go @@ -50,10 +50,11 @@ var routers = map[string]func(w http.ResponseWriter, r *http.Request){ "/setting/userdata/location/update": handleUserDataLocationUpdate, // events - "/on/focus/lost": handleOnFocusLost, - "/on/ready": handleOnUIReady, - "/on/show": handleOnShow, - "/on/hide": handleOnHide, + "/on/focus/lost": handleOnFocusLost, + "/on/ready": handleOnUIReady, + "/on/show": handleOnShow, + "/on/querybox/focus": handleOnQueryBoxFocus, + "/on/hide": handleOnHide, // lang "/lang/available": handleLangAvailable, @@ -699,6 +700,12 @@ func handleOnShow(w http.ResponseWriter, r *http.Request) { writeSuccessResponse(w, "") } +func handleOnQueryBoxFocus(w http.ResponseWriter, r *http.Request) { + ctx := util.NewTraceContext() + GetUIManager().PostOnQueryBoxFocus(ctx) + writeSuccessResponse(w, "") +} + func handleOnHide(w http.ResponseWriter, r *http.Request) { ctx := util.NewTraceContext() diff --git a/wox.ui.flutter/wox/lib/api/wox_api.dart b/wox.ui.flutter/wox/lib/api/wox_api.dart index 322740d4..eda02d7b 100644 --- a/wox.ui.flutter/wox/lib/api/wox_api.dart +++ b/wox.ui.flutter/wox/lib/api/wox_api.dart @@ -99,6 +99,10 @@ class WoxApi { await WoxHttpUtil.instance.postData("/on/show", {}); } + Future onQueryBoxFocus() async { + await WoxHttpUtil.instance.postData("/on/querybox/focus", {}); + } + Future onHide(PlainQuery query) async { await WoxHttpUtil.instance.postData("/on/hide", { "query": query.toJson(), diff --git a/wox.ui.flutter/wox/lib/components/wox_image_view.dart b/wox.ui.flutter/wox/lib/components/wox_image_view.dart index 545fd198..bf92b8b9 100644 --- a/wox.ui.flutter/wox/lib/components/wox_image_view.dart +++ b/wox.ui.flutter/wox/lib/components/wox_image_view.dart @@ -9,6 +9,21 @@ import 'package:wox/entity/wox_image.dart'; import 'package:wox/entity/wox_theme.dart'; import 'package:wox/enums/wox_image_type_enum.dart'; +// Image cache to prevent flickering during refreshes +class _ImageCache { + static final Map _cache = {}; + + static Widget? get(String key) => _cache[key]; + + static void put(String key, Widget widget) { + if (_cache.length > 100) { + // Limit cache size + _cache.clear(); + } + _cache[key] = widget; + } +} + class WoxImageView extends StatelessWidget { final WoxImage woxImage; final double? width; @@ -18,8 +33,19 @@ class WoxImageView extends StatelessWidget { @override Widget build(BuildContext context) { + // Create cache key based on image data and dimensions + final cacheKey = '${woxImage.imageType}_${woxImage.imageData}_${width}_$height'; + + // Check cache first to prevent flickering + final cachedWidget = _ImageCache.get(cacheKey); + if (cachedWidget != null) { + return cachedWidget; + } + + Widget imageWidget; + if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_URL.code) { - return Image.network( + imageWidget = Image.network( woxImage.imageData, width: width, height: height, @@ -31,26 +57,33 @@ class WoxImageView extends StatelessWidget { } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_ABSOLUTE_PATH.code) { // check if file exists if (!File(woxImage.imageData).existsSync()) { - return const SizedBox(width: 24, height: 24); + imageWidget = const SizedBox(width: 24, height: 24); + } else { + imageWidget = Image.file(File(woxImage.imageData), width: width, height: height); } - - return Image.file(File(woxImage.imageData), width: width, height: height); } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_SVG.code) { - return SvgPicture.string(woxImage.imageData, width: width, height: height); + imageWidget = SvgPicture.string(woxImage.imageData, width: width, height: height); } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_EMOJI.code) { - return Text(woxImage.imageData, style: TextStyle(fontSize: width)); + imageWidget = Text(woxImage.imageData, style: TextStyle(fontSize: width)); } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_LOTTIE.code) { final bytes = utf8.encode(woxImage.imageData); - return Lottie.memory(bytes, width: width, height: height); + imageWidget = Lottie.memory(bytes, width: width, height: height); } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_THEME.code) { - return WoxThemeIconView(theme: WoxTheme.fromJson(jsonDecode(woxImage.imageData)), width: width, height: height); + imageWidget = WoxThemeIconView(theme: WoxTheme.fromJson(jsonDecode(woxImage.imageData)), width: width, height: height); } else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_BASE64.code) { if (!woxImage.imageData.contains(";base64,")) { - return Text("Invalid image data: ${woxImage.imageData}", style: const TextStyle(color: Colors.red)); + imageWidget = Text("Invalid image data: ${woxImage.imageData}", style: const TextStyle(color: Colors.red)); + } else { + final imageData = woxImage.imageData.split(";base64,")[1]; + imageWidget = Image.memory(base64Decode(imageData), width: width, height: height, fit: BoxFit.contain); } - final imageData = woxImage.imageData.split(";base64,")[1]; - return Image.memory(base64Decode(imageData), width: width, height: height, fit: BoxFit.contain); + } else { + imageWidget = const SizedBox(width: 24, height: 24); } - return const SizedBox(width: 24, height: 24); + + // Cache the widget to prevent future rebuilds + _ImageCache.put(cacheKey, imageWidget); + + return imageWidget; } } diff --git a/wox.ui.flutter/wox/lib/components/wox_list_view.dart b/wox.ui.flutter/wox/lib/components/wox_list_view.dart index 91401c8a..01c7dd83 100644 --- a/wox.ui.flutter/wox/lib/components/wox_list_view.dart +++ b/wox.ui.flutter/wox/lib/components/wox_list_view.dart @@ -57,9 +57,6 @@ class WoxListView extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), itemCount: controller.items.length, itemExtent: WoxThemeUtil.instance.getResultListViewHeightByCount(1), - addAutomaticKeepAlives: false, - addRepaintBoundaries: false, - addSemanticIndexes: false, itemBuilder: (context, index) { var item = controller.items[index]; return MouseRegion( diff --git a/wox.ui.flutter/wox/lib/controllers/wox_launcher_controller.dart b/wox.ui.flutter/wox/lib/controllers/wox_launcher_controller.dart index 8db7fa8f..402f0bab 100644 --- a/wox.ui.flutter/wox/lib/controllers/wox_launcher_controller.dart +++ b/wox.ui.flutter/wox/lib/controllers/wox_launcher_controller.dart @@ -124,6 +124,14 @@ class WoxLauncherController extends GetxController { tag: 'action', ); + // Add focus listener to query box + queryBoxFocusNode.addListener(() { + if (queryBoxFocusNode.hasFocus) { + // Call API when query box gains focus + WoxApi.instance.onQueryBoxFocus(); + } + }); + // Initialize doctor check info doctorCheckInfo.value = DoctorCheckInfo.empty(); } @@ -726,7 +734,6 @@ class WoxLauncherController extends GetxController { resizeHeight(); } - // update actions list var actions = result.value.data.actions.map((e) => WoxListItem.fromResultAction(e)).toList(); var oldActionIndex = actionListViewController.activeIndex.value; var oldActionCount = actionListViewController.items.length;