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.
This commit is contained in:
qianlifeng 2025-05-26 09:22:22 +08:00
parent f53df14bf8
commit 8e5adf7c5c
No known key found for this signature in database
10 changed files with 77 additions and 25 deletions

View File

@ -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",

View File

@ -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",

View File

@ -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": "Ярлыки запросов",

View File

@ -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": "查询缩写",

View File

@ -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()))

View File

@ -53,6 +53,7 @@ var routers = map[string]func(w http.ResponseWriter, r *http.Request){
"/on/focus/lost": handleOnFocusLost,
"/on/ready": handleOnUIReady,
"/on/show": handleOnShow,
"/on/querybox/focus": handleOnQueryBoxFocus,
"/on/hide": handleOnHide,
// lang
@ -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()

View File

@ -99,6 +99,10 @@ class WoxApi {
await WoxHttpUtil.instance.postData("/on/show", {});
}
Future<void> onQueryBoxFocus() async {
await WoxHttpUtil.instance.postData("/on/querybox/focus", {});
}
Future<void> onHide(PlainQuery query) async {
await WoxHttpUtil.instance.postData("/on/hide", {
"query": query.toJson(),

View File

@ -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<String, Widget> _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];
return Image.memory(base64Decode(imageData), width: width, height: height, fit: BoxFit.contain);
imageWidget = Image.memory(base64Decode(imageData), width: width, height: height, fit: BoxFit.contain);
}
return const SizedBox(width: 24, height: 24);
} else {
imageWidget = const SizedBox(width: 24, height: 24);
}
// Cache the widget to prevent future rebuilds
_ImageCache.put(cacheKey, imageWidget);
return imageWidget;
}
}

View File

@ -57,9 +57,6 @@ class WoxListView<T> 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(

View File

@ -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;