mirror of https://github.com/Wox-launcher/Wox
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:
parent
f53df14bf8
commit
8e5adf7c5c
|
@ -23,7 +23,7 @@
|
||||||
"ui_show_position_active_screen": "Active screen",
|
"ui_show_position_active_screen": "Active screen",
|
||||||
"ui_show_position_last_location": "Last location",
|
"ui_show_position_last_location": "Last location",
|
||||||
"ui_switch_input_method_abc": "Switch to ABC",
|
"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_lang": "Language",
|
||||||
"ui_query_hotkeys": "Query Hotkeys",
|
"ui_query_hotkeys": "Query Hotkeys",
|
||||||
"ui_query_shortcuts": "Query Shortcuts",
|
"ui_query_shortcuts": "Query Shortcuts",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"ui_show_position_active_screen": "Tela ativa",
|
"ui_show_position_active_screen": "Tela ativa",
|
||||||
"ui_show_position_last_location": "Última posição",
|
"ui_show_position_last_location": "Última posição",
|
||||||
"ui_switch_input_method_abc": "Alternar para ABC",
|
"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_lang": "Idioma",
|
||||||
"ui_query_hotkeys": "Teclas de atalho para consulta",
|
"ui_query_hotkeys": "Teclas de atalho para consulta",
|
||||||
"ui_query_shortcuts": "Atalhos de consulta",
|
"ui_query_shortcuts": "Atalhos de consulta",
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"ui_show_position_active_screen": "Активный экран",
|
"ui_show_position_active_screen": "Активный экран",
|
||||||
"ui_show_position_last_location": "Последнее положение",
|
"ui_show_position_last_location": "Последнее положение",
|
||||||
"ui_switch_input_method_abc": "Переключить на ABC",
|
"ui_switch_input_method_abc": "Переключить на ABC",
|
||||||
"ui_switch_input_method_abc_tips": "При выборе метод ввода будет переключен на английский",
|
"ui_switch_input_method_abc_tips": "При выборе метод ввода будет переключен на английский при получении фокуса полем запроса",
|
||||||
"ui_lang": "Язык",
|
"ui_lang": "Язык",
|
||||||
"ui_query_hotkeys": "Горячие клавиши запроса",
|
"ui_query_hotkeys": "Горячие клавиши запроса",
|
||||||
"ui_query_shortcuts": "Ярлыки запросов",
|
"ui_query_shortcuts": "Ярлыки запросов",
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
"ui_show_position_active_screen": "活动屏幕",
|
"ui_show_position_active_screen": "活动屏幕",
|
||||||
"ui_show_position_last_location": "上次位置",
|
"ui_show_position_last_location": "上次位置",
|
||||||
"ui_switch_input_method_abc": "切换输入法",
|
"ui_switch_input_method_abc": "切换输入法",
|
||||||
"ui_switch_input_method_abc_tips": "选中后,输入法将切换到英文",
|
"ui_switch_input_method_abc_tips": "选中后,查询框获得焦点时输入法将切换到英文",
|
||||||
"ui_lang": "语言",
|
"ui_lang": "语言",
|
||||||
"ui_query_hotkeys": "查询快捷",
|
"ui_query_hotkeys": "查询快捷",
|
||||||
"ui_query_shortcuts": "查询缩写",
|
"ui_query_shortcuts": "查询缩写",
|
||||||
|
|
|
@ -419,9 +419,13 @@ func (m *Manager) PostUIReady(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Manager) PostOnShow(ctx context.Context) {
|
func (m *Manager) PostOnShow(ctx context.Context) {
|
||||||
|
//no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Manager) PostOnQueryBoxFocus(ctx context.Context) {
|
||||||
woxSetting := setting.GetSettingManager().GetWoxSetting(ctx)
|
woxSetting := setting.GetSettingManager().GetWoxSetting(ctx)
|
||||||
if woxSetting.SwitchInputMethodABC {
|
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()
|
switchErr := ime.SwitchInputMethodABC()
|
||||||
if switchErr != nil {
|
if switchErr != nil {
|
||||||
logger.Error(ctx, fmt.Sprintf("failed to switch input method to ABC: %s", switchErr.Error()))
|
logger.Error(ctx, fmt.Sprintf("failed to switch input method to ABC: %s", switchErr.Error()))
|
||||||
|
|
|
@ -50,10 +50,11 @@ var routers = map[string]func(w http.ResponseWriter, r *http.Request){
|
||||||
"/setting/userdata/location/update": handleUserDataLocationUpdate,
|
"/setting/userdata/location/update": handleUserDataLocationUpdate,
|
||||||
|
|
||||||
// events
|
// events
|
||||||
"/on/focus/lost": handleOnFocusLost,
|
"/on/focus/lost": handleOnFocusLost,
|
||||||
"/on/ready": handleOnUIReady,
|
"/on/ready": handleOnUIReady,
|
||||||
"/on/show": handleOnShow,
|
"/on/show": handleOnShow,
|
||||||
"/on/hide": handleOnHide,
|
"/on/querybox/focus": handleOnQueryBoxFocus,
|
||||||
|
"/on/hide": handleOnHide,
|
||||||
|
|
||||||
// lang
|
// lang
|
||||||
"/lang/available": handleLangAvailable,
|
"/lang/available": handleLangAvailable,
|
||||||
|
@ -699,6 +700,12 @@ func handleOnShow(w http.ResponseWriter, r *http.Request) {
|
||||||
writeSuccessResponse(w, "")
|
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) {
|
func handleOnHide(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := util.NewTraceContext()
|
ctx := util.NewTraceContext()
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,10 @@ class WoxApi {
|
||||||
await WoxHttpUtil.instance.postData("/on/show", {});
|
await WoxHttpUtil.instance.postData("/on/show", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> onQueryBoxFocus() async {
|
||||||
|
await WoxHttpUtil.instance.postData("/on/querybox/focus", {});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> onHide(PlainQuery query) async {
|
Future<void> onHide(PlainQuery query) async {
|
||||||
await WoxHttpUtil.instance.postData("/on/hide", {
|
await WoxHttpUtil.instance.postData("/on/hide", {
|
||||||
"query": query.toJson(),
|
"query": query.toJson(),
|
||||||
|
|
|
@ -9,6 +9,21 @@ import 'package:wox/entity/wox_image.dart';
|
||||||
import 'package:wox/entity/wox_theme.dart';
|
import 'package:wox/entity/wox_theme.dart';
|
||||||
import 'package:wox/enums/wox_image_type_enum.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 {
|
class WoxImageView extends StatelessWidget {
|
||||||
final WoxImage woxImage;
|
final WoxImage woxImage;
|
||||||
final double? width;
|
final double? width;
|
||||||
|
@ -18,8 +33,19 @@ class WoxImageView extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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) {
|
if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_URL.code) {
|
||||||
return Image.network(
|
imageWidget = Image.network(
|
||||||
woxImage.imageData,
|
woxImage.imageData,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
@ -31,26 +57,33 @@ class WoxImageView extends StatelessWidget {
|
||||||
} else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_ABSOLUTE_PATH.code) {
|
} else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_ABSOLUTE_PATH.code) {
|
||||||
// check if file exists
|
// check if file exists
|
||||||
if (!File(woxImage.imageData).existsSync()) {
|
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) {
|
} 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) {
|
} 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) {
|
} else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_LOTTIE.code) {
|
||||||
final bytes = utf8.encode(woxImage.imageData);
|
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) {
|
} 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) {
|
} else if (woxImage.imageType == WoxImageTypeEnum.WOX_IMAGE_TYPE_BASE64.code) {
|
||||||
if (!woxImage.imageData.contains(";base64,")) {
|
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];
|
} else {
|
||||||
return Image.memory(base64Decode(imageData), width: width, height: height, fit: BoxFit.contain);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,9 +57,6 @@ class WoxListView<T> extends StatelessWidget {
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: controller.items.length,
|
itemCount: controller.items.length,
|
||||||
itemExtent: WoxThemeUtil.instance.getResultListViewHeightByCount(1),
|
itemExtent: WoxThemeUtil.instance.getResultListViewHeightByCount(1),
|
||||||
addAutomaticKeepAlives: false,
|
|
||||||
addRepaintBoundaries: false,
|
|
||||||
addSemanticIndexes: false,
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
var item = controller.items[index];
|
var item = controller.items[index];
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
|
|
|
@ -124,6 +124,14 @@ class WoxLauncherController extends GetxController {
|
||||||
tag: 'action',
|
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
|
// Initialize doctor check info
|
||||||
doctorCheckInfo.value = DoctorCheckInfo.empty();
|
doctorCheckInfo.value = DoctorCheckInfo.empty();
|
||||||
}
|
}
|
||||||
|
@ -726,7 +734,6 @@ class WoxLauncherController extends GetxController {
|
||||||
resizeHeight();
|
resizeHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
// update actions list
|
|
||||||
var actions = result.value.data.actions.map((e) => WoxListItem.fromResultAction(e)).toList();
|
var actions = result.value.data.actions.map((e) => WoxListItem.fromResultAction(e)).toList();
|
||||||
var oldActionIndex = actionListViewController.activeIndex.value;
|
var oldActionIndex = actionListViewController.activeIndex.value;
|
||||||
var oldActionCount = actionListViewController.items.length;
|
var oldActionCount = actionListViewController.items.length;
|
||||||
|
|
Loading…
Reference in New Issue