mirror of https://github.com/Wox-launcher/Wox
Add hotkey support for actions
This update introduces hotkey support, allowing actions and queries to be triggered via hotkeys. New fields and helper methods were added to handle hotkey parsing and execution. Additionally, modifications were made to handle key events and map them to the corresponding actions.
This commit is contained in:
parent
d85832c346
commit
889bfb2020
|
@ -32,7 +32,7 @@
|
|||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wox-launcher/wox-plugin": "^0.0.78",
|
||||
"@wox-launcher/wox-plugin": "^0.0.79",
|
||||
"dayjs": "^1.11.9",
|
||||
"promise-deferred": "^2.0.4",
|
||||
"winston": "^3.10.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@wox-launcher/wox-plugin",
|
||||
"version": "0.0.78",
|
||||
"version": "0.0.79",
|
||||
"description": "All nodejs plugin for Wox should use types in this package",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -1,246 +1,253 @@
|
|||
import { MetadataCommand, PluginSettingDefinitionItem } from "./setting.js"
|
||||
import { AI } from "./ai.js"
|
||||
import {MetadataCommand, PluginSettingDefinitionItem} from "./setting.js"
|
||||
import {AI} from "./ai.js"
|
||||
|
||||
export type MapString = { [key: string]: string }
|
||||
|
||||
export type Platform = "windows" | "darwin" | "linux"
|
||||
|
||||
export interface Plugin {
|
||||
init: (ctx: Context, initParams: PluginInitParams) => Promise<void>
|
||||
query: (ctx: Context, query: Query) => Promise<Result[]>
|
||||
init: (ctx: Context, initParams: PluginInitParams) => Promise<void>
|
||||
query: (ctx: Context, query: Query) => Promise<Result[]>
|
||||
}
|
||||
|
||||
export interface Selection {
|
||||
Type: "text" | "file"
|
||||
// Only available when Type is text
|
||||
Text: string
|
||||
// Only available when Type is file
|
||||
FilePaths: string[]
|
||||
Type: "text" | "file"
|
||||
// Only available when Type is text
|
||||
Text: string
|
||||
// Only available when Type is file
|
||||
FilePaths: string[]
|
||||
}
|
||||
|
||||
export interface QueryEnv {
|
||||
/**
|
||||
* Active window title when user query
|
||||
*/
|
||||
ActiveWindowTitle: string
|
||||
/**
|
||||
* Active window title when user query
|
||||
*/
|
||||
ActiveWindowTitle: string
|
||||
}
|
||||
|
||||
export interface Query {
|
||||
/**
|
||||
* By default, Wox will only pass input query to plugin.
|
||||
* plugin author need to enable MetadataFeatureQuerySelection feature to handle selection query
|
||||
*/
|
||||
Type: "input" | "selection"
|
||||
/**
|
||||
* Raw query, this includes trigger keyword if it has
|
||||
* We didn't recommend use this property directly. You should always use Search property.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
RawQuery: string
|
||||
/**
|
||||
* Trigger keyword of a query. It can be empty if user is using global trigger keyword.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
TriggerKeyword?: string
|
||||
/**
|
||||
* Command part of a query.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
Command?: string
|
||||
/**
|
||||
* Search part of a query.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
Search: string
|
||||
/**
|
||||
* By default, Wox will only pass input query to plugin.
|
||||
* plugin author need to enable MetadataFeatureQuerySelection feature to handle selection query
|
||||
*/
|
||||
Type: "input" | "selection"
|
||||
/**
|
||||
* Raw query, this includes trigger keyword if it has
|
||||
* We didn't recommend use this property directly. You should always use Search property.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
RawQuery: string
|
||||
/**
|
||||
* Trigger keyword of a query. It can be empty if user is using global trigger keyword.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
TriggerKeyword?: string
|
||||
/**
|
||||
* Command part of a query.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
Command?: string
|
||||
/**
|
||||
* Search part of a query.
|
||||
*
|
||||
* NOTE: Only available when query type is input
|
||||
*/
|
||||
Search: string
|
||||
|
||||
/**
|
||||
* User selected or drag-drop data, can be text or file or image etc
|
||||
*
|
||||
* NOTE: Only available when query type is selection
|
||||
*/
|
||||
Selection: Selection
|
||||
/**
|
||||
* User selected or drag-drop data, can be text or file or image etc
|
||||
*
|
||||
* NOTE: Only available when query type is selection
|
||||
*/
|
||||
Selection: Selection
|
||||
|
||||
/**
|
||||
* Additional query environment data
|
||||
* expose more context env data to plugin, E.g. plugin A only show result when active window title is "Chrome"
|
||||
*/
|
||||
Env: QueryEnv
|
||||
/**
|
||||
* Additional query environment data
|
||||
* expose more context env data to plugin, E.g. plugin A only show result when active window title is "Chrome"
|
||||
*/
|
||||
Env: QueryEnv
|
||||
|
||||
/**
|
||||
* Whether current query is global query
|
||||
*/
|
||||
IsGlobalQuery(): boolean
|
||||
/**
|
||||
* Whether current query is global query
|
||||
*/
|
||||
IsGlobalQuery(): boolean
|
||||
}
|
||||
|
||||
export interface Result {
|
||||
Id?: string
|
||||
Title: string
|
||||
SubTitle?: string
|
||||
Icon: WoxImage
|
||||
Preview?: WoxPreview
|
||||
Score?: number
|
||||
Group?: string
|
||||
GroupScore?: number
|
||||
Tails?: ResultTail[]
|
||||
ContextData?: string
|
||||
Actions?: ResultAction[]
|
||||
// refresh result after specified interval, in milliseconds. If this value is 0, Wox will not refresh this result
|
||||
// interval can only divisible by 100, if not, Wox will use the nearest number which is divisible by 100
|
||||
// E.g. if you set 123, Wox will use 200, if you set 1234, Wox will use 1300
|
||||
RefreshInterval?: number
|
||||
// refresh result by calling OnRefresh function
|
||||
OnRefresh?: (current: RefreshableResult) => Promise<RefreshableResult>
|
||||
Id?: string
|
||||
Title: string
|
||||
SubTitle?: string
|
||||
Icon: WoxImage
|
||||
Preview?: WoxPreview
|
||||
Score?: number
|
||||
Group?: string
|
||||
GroupScore?: number
|
||||
Tails?: ResultTail[]
|
||||
ContextData?: string
|
||||
Actions?: ResultAction[]
|
||||
// refresh result after specified interval, in milliseconds. If this value is 0, Wox will not refresh this result
|
||||
// interval can only divisible by 100, if not, Wox will use the nearest number which is divisible by 100
|
||||
// E.g. if you set 123, Wox will use 200, if you set 1234, Wox will use 1300
|
||||
RefreshInterval?: number
|
||||
// refresh result by calling OnRefresh function
|
||||
OnRefresh?: (current: RefreshableResult) => Promise<RefreshableResult>
|
||||
}
|
||||
|
||||
export interface ResultTail {
|
||||
Type: "text" | "image"
|
||||
Text?: string
|
||||
Image?: WoxImage
|
||||
Type: "text" | "image"
|
||||
Text?: string
|
||||
Image?: WoxImage
|
||||
}
|
||||
|
||||
export interface RefreshableResult {
|
||||
Title: string
|
||||
SubTitle: string
|
||||
Icon: WoxImage
|
||||
Preview: WoxPreview
|
||||
ContextData: string
|
||||
RefreshInterval: number
|
||||
Title: string
|
||||
SubTitle: string
|
||||
Icon: WoxImage
|
||||
Preview: WoxPreview
|
||||
ContextData: string
|
||||
RefreshInterval: number
|
||||
}
|
||||
|
||||
export interface ResultAction {
|
||||
/**
|
||||
* Result id, should be unique. It's optional, if you don't set it, Wox will assign a random id for you
|
||||
*/
|
||||
Id?: string
|
||||
Name: string
|
||||
Icon?: WoxImage
|
||||
/**
|
||||
* If true, Wox will use this action as default action. There can be only one default action in results
|
||||
* This can be omitted, if you don't set it, Wox will use the first action as default action
|
||||
*/
|
||||
IsDefault?: boolean
|
||||
/**
|
||||
* If true, Wox will not hide after user select this result
|
||||
*/
|
||||
PreventHideAfterAction?: boolean
|
||||
Action: (actionContext: ActionContext) => Promise<void>
|
||||
/**
|
||||
* Result id, should be unique. It's optional, if you don't set it, Wox will assign a random id for you
|
||||
*/
|
||||
Id?: string
|
||||
Name: string
|
||||
Icon?: WoxImage
|
||||
/**
|
||||
* If true, Wox will use this action as default action. There can be only one default action in results
|
||||
* This can be omitted, if you don't set it, Wox will use the first action as default action
|
||||
*/
|
||||
IsDefault?: boolean
|
||||
/**
|
||||
* If true, Wox will not hide after user select this result
|
||||
*/
|
||||
PreventHideAfterAction?: boolean
|
||||
Action: (actionContext: ActionContext) => Promise<void>
|
||||
/**
|
||||
* Hotkey to trigger this action. E.g. "ctrl+Shift+Space", "Ctrl+1", "Command+K"
|
||||
* Case insensitive, space insensitive
|
||||
*
|
||||
* If IsDefault is true, Hotkey will be set to enter key by default
|
||||
*/
|
||||
Hotkey?: string
|
||||
}
|
||||
|
||||
export interface ActionContext {
|
||||
ContextData: string
|
||||
ContextData: string
|
||||
}
|
||||
|
||||
export interface PluginInitParams {
|
||||
API: PublicAPI
|
||||
PluginDirectory: string
|
||||
API: PublicAPI
|
||||
PluginDirectory: string
|
||||
}
|
||||
|
||||
export interface ChangeQueryParam {
|
||||
QueryType: "input" | "selection"
|
||||
QueryText?: string
|
||||
QuerySelection?: Selection
|
||||
QueryType: "input" | "selection"
|
||||
QueryText?: string
|
||||
QuerySelection?: Selection
|
||||
}
|
||||
|
||||
export interface PublicAPI {
|
||||
/**
|
||||
* Change Wox query
|
||||
*/
|
||||
ChangeQuery: (ctx: Context, query: ChangeQueryParam) => Promise<void>
|
||||
/**
|
||||
* Change Wox query
|
||||
*/
|
||||
ChangeQuery: (ctx: Context, query: ChangeQueryParam) => Promise<void>
|
||||
|
||||
/**
|
||||
* Hide Wox
|
||||
*/
|
||||
HideApp: (ctx: Context) => Promise<void>
|
||||
/**
|
||||
* Hide Wox
|
||||
*/
|
||||
HideApp: (ctx: Context) => Promise<void>
|
||||
|
||||
/**
|
||||
* Show Wox
|
||||
*/
|
||||
ShowApp: (ctx: Context) => Promise<void>
|
||||
/**
|
||||
* Show Wox
|
||||
*/
|
||||
ShowApp: (ctx: Context) => Promise<void>
|
||||
|
||||
/**
|
||||
* Notify message
|
||||
*/
|
||||
Notify: (ctx: Context, title: string, description?: string) => Promise<void>
|
||||
/**
|
||||
* Notify message
|
||||
*/
|
||||
Notify: (ctx: Context, title: string, description?: string) => Promise<void>
|
||||
|
||||
/**
|
||||
* Write log
|
||||
*/
|
||||
Log: (ctx: Context, level: "Info" | "Error" | "Debug" | "Warning", msg: string) => Promise<void>
|
||||
/**
|
||||
* Write log
|
||||
*/
|
||||
Log: (ctx: Context, level: "Info" | "Error" | "Debug" | "Warning", msg: string) => Promise<void>
|
||||
|
||||
/**
|
||||
* Get translation of current language
|
||||
*/
|
||||
GetTranslation: (ctx: Context, key: string) => Promise<string>
|
||||
/**
|
||||
* Get translation of current language
|
||||
*/
|
||||
GetTranslation: (ctx: Context, key: string) => Promise<string>
|
||||
|
||||
/**
|
||||
* Get customized setting
|
||||
*
|
||||
* will try to get platform specific setting first, if not found, will try to get global setting
|
||||
*/
|
||||
GetSetting: (ctx: Context, key: string) => Promise<string>
|
||||
/**
|
||||
* Get customized setting
|
||||
*
|
||||
* will try to get platform specific setting first, if not found, will try to get global setting
|
||||
*/
|
||||
GetSetting: (ctx: Context, key: string) => Promise<string>
|
||||
|
||||
/**
|
||||
* Save customized setting
|
||||
*
|
||||
* @isPlatformSpecific If true, setting will be only saved in current platform. If false, setting will be available in all platforms
|
||||
*/
|
||||
SaveSetting: (ctx: Context, key: string, value: string, isPlatformSpecific: boolean) => Promise<void>
|
||||
/**
|
||||
* Save customized setting
|
||||
*
|
||||
* @isPlatformSpecific If true, setting will be only saved in current platform. If false, setting will be available in all platforms
|
||||
*/
|
||||
SaveSetting: (ctx: Context, key: string, value: string, isPlatformSpecific: boolean) => Promise<void>
|
||||
|
||||
/**
|
||||
* Register setting changed callback
|
||||
*/
|
||||
OnSettingChanged: (ctx: Context, callback: (key: string, value: string) => void) => Promise<void>
|
||||
/**
|
||||
* Register setting changed callback
|
||||
*/
|
||||
OnSettingChanged: (ctx: Context, callback: (key: string, value: string) => void) => Promise<void>
|
||||
|
||||
/**
|
||||
* Get dynamic setting definition
|
||||
*/
|
||||
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => PluginSettingDefinitionItem) => Promise<void>
|
||||
/**
|
||||
* Get dynamic setting definition
|
||||
*/
|
||||
OnGetDynamicSetting: (ctx: Context, callback: (key: string) => PluginSettingDefinitionItem) => Promise<void>
|
||||
|
||||
/**
|
||||
* Register deep link callback
|
||||
*/
|
||||
OnDeepLink: (ctx: Context, callback: (arguments: MapString) => void) => Promise<void>
|
||||
/**
|
||||
* Register deep link callback
|
||||
*/
|
||||
OnDeepLink: (ctx: Context, callback: (arguments: MapString) => void) => Promise<void>
|
||||
|
||||
/**
|
||||
* Register on load event
|
||||
*/
|
||||
OnUnload: (ctx: Context, callback: () => Promise<void>) => Promise<void>
|
||||
/**
|
||||
* Register on load event
|
||||
*/
|
||||
OnUnload: (ctx: Context, callback: () => Promise<void>) => Promise<void>
|
||||
|
||||
/**
|
||||
* Register query commands
|
||||
*/
|
||||
RegisterQueryCommands: (ctx: Context, commands: MetadataCommand[]) => Promise<void>
|
||||
/**
|
||||
* Register query commands
|
||||
*/
|
||||
RegisterQueryCommands: (ctx: Context, commands: MetadataCommand[]) => Promise<void>
|
||||
|
||||
/**
|
||||
* Chat using LLM
|
||||
*/
|
||||
LLMStream: (ctx: Context, conversations: AI.Conversation[], callback: AI.ChatStreamFunc) => Promise<void>
|
||||
/**
|
||||
* Chat using LLM
|
||||
*/
|
||||
LLMStream: (ctx: Context, conversations: AI.Conversation[], callback: AI.ChatStreamFunc) => Promise<void>
|
||||
}
|
||||
|
||||
export type WoxImageType = "absolute" | "relative" | "base64" | "svg" | "url" | "emoji" | "lottie"
|
||||
|
||||
export interface WoxImage {
|
||||
ImageType: WoxImageType
|
||||
ImageData: string
|
||||
ImageType: WoxImageType
|
||||
ImageData: string
|
||||
}
|
||||
|
||||
export type WoxPreviewType = "markdown" | "text" | "image" | "url" | "file"
|
||||
|
||||
export interface WoxPreview {
|
||||
PreviewType: WoxPreviewType
|
||||
PreviewData: string
|
||||
PreviewProperties: Record<string, string>
|
||||
PreviewType: WoxPreviewType
|
||||
PreviewData: string
|
||||
PreviewProperties: Record<string, string>
|
||||
}
|
||||
|
||||
export declare interface Context {
|
||||
Values: { [key: string]: string }
|
||||
Get: (key: string) => string | undefined
|
||||
Set: (key: string, value: string) => void
|
||||
Exists: (key: string) => boolean
|
||||
Values: { [key: string]: string }
|
||||
Get: (key: string) => string | undefined
|
||||
Set: (key: string, value: string) => void
|
||||
Exists: (key: string) => boolean
|
||||
}
|
||||
|
||||
export function NewContext(): Context
|
||||
|
|
|
@ -178,7 +178,7 @@ class _WoxSettingPluginTableUpdateState extends State<WoxSettingPluginTableUpdat
|
|||
);
|
||||
case PluginSettingValueType.pluginSettingValueTableColumnTypeHotkey:
|
||||
return WoxHotkeyRecorder(
|
||||
hotkey: WoxHotkey.parseHotkey(getValue(column.key)),
|
||||
hotkey: WoxHotkey.parseHotkeyFromString(getValue(column.key)),
|
||||
onHotKeyRecorded: (hotkey) {
|
||||
updateValue(column.key, hotkey);
|
||||
setState(() {});
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:uuid/v4.dart';
|
||||
import 'package:wox/api/wox_api.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/utils/colors.dart';
|
||||
import 'package:wox/utils/log.dart';
|
||||
|
||||
|
@ -41,107 +42,9 @@ class _WoxHotkeyRecorderState extends State<WoxHotkeyRecorder> {
|
|||
HardwareKeyboard.instance.removeHandler(_handleKeyEvent);
|
||||
}
|
||||
|
||||
String getHotkeyString(HotKey hotKey) {
|
||||
var modifiers = [];
|
||||
if (hotKey.modifiers != null) {
|
||||
for (var modifier in hotKey.modifiers!) {
|
||||
if (modifier == HotKeyModifier.shift) {
|
||||
modifiers.add("shift");
|
||||
} else if (modifier == HotKeyModifier.control) {
|
||||
modifiers.add("ctrl");
|
||||
} else if (modifier == HotKeyModifier.alt) {
|
||||
if (Platform.isMacOS) {
|
||||
modifiers.add("option");
|
||||
} else {
|
||||
modifiers.add("alt");
|
||||
}
|
||||
} else if (modifier == HotKeyModifier.meta) {
|
||||
if (Platform.isMacOS) {
|
||||
modifiers.add("cmd");
|
||||
} else {
|
||||
modifiers.add("win");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyStr = hotKey.key.keyLabel.toLowerCase();
|
||||
if (hotKey.key == PhysicalKeyboardKey.space) {
|
||||
keyStr = "space";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.enter) {
|
||||
keyStr = "enter";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.backspace) {
|
||||
keyStr = "backspace";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.delete) {
|
||||
keyStr = "delete";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowLeft) {
|
||||
keyStr = "left";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowDown) {
|
||||
keyStr = "down";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowRight) {
|
||||
keyStr = "right";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowUp) {
|
||||
keyStr = "up";
|
||||
}
|
||||
|
||||
return "${modifiers.join("+")}+$keyStr";
|
||||
}
|
||||
|
||||
bool isAllowedKey(PhysicalKeyboardKey key) {
|
||||
var allowedKeys = [
|
||||
PhysicalKeyboardKey.keyA,
|
||||
PhysicalKeyboardKey.keyB,
|
||||
PhysicalKeyboardKey.keyC,
|
||||
PhysicalKeyboardKey.keyD,
|
||||
PhysicalKeyboardKey.keyE,
|
||||
PhysicalKeyboardKey.keyF,
|
||||
PhysicalKeyboardKey.keyG,
|
||||
PhysicalKeyboardKey.keyH,
|
||||
PhysicalKeyboardKey.keyI,
|
||||
PhysicalKeyboardKey.keyJ,
|
||||
PhysicalKeyboardKey.keyK,
|
||||
PhysicalKeyboardKey.keyL,
|
||||
PhysicalKeyboardKey.keyM,
|
||||
PhysicalKeyboardKey.keyN,
|
||||
PhysicalKeyboardKey.keyO,
|
||||
PhysicalKeyboardKey.keyP,
|
||||
PhysicalKeyboardKey.keyQ,
|
||||
PhysicalKeyboardKey.keyR,
|
||||
PhysicalKeyboardKey.keyS,
|
||||
PhysicalKeyboardKey.keyT,
|
||||
PhysicalKeyboardKey.keyU,
|
||||
PhysicalKeyboardKey.keyV,
|
||||
PhysicalKeyboardKey.keyW,
|
||||
PhysicalKeyboardKey.keyX,
|
||||
PhysicalKeyboardKey.keyY,
|
||||
PhysicalKeyboardKey.keyZ,
|
||||
PhysicalKeyboardKey.digit1,
|
||||
PhysicalKeyboardKey.digit2,
|
||||
PhysicalKeyboardKey.digit3,
|
||||
PhysicalKeyboardKey.digit4,
|
||||
PhysicalKeyboardKey.digit5,
|
||||
PhysicalKeyboardKey.digit6,
|
||||
PhysicalKeyboardKey.digit7,
|
||||
PhysicalKeyboardKey.digit8,
|
||||
PhysicalKeyboardKey.digit9,
|
||||
PhysicalKeyboardKey.digit0,
|
||||
PhysicalKeyboardKey.space,
|
||||
PhysicalKeyboardKey.enter,
|
||||
PhysicalKeyboardKey.backspace,
|
||||
PhysicalKeyboardKey.delete,
|
||||
PhysicalKeyboardKey.arrowLeft,
|
||||
PhysicalKeyboardKey.arrowDown,
|
||||
PhysicalKeyboardKey.arrowRight,
|
||||
PhysicalKeyboardKey.arrowUp,
|
||||
];
|
||||
|
||||
return allowedKeys.contains(key);
|
||||
}
|
||||
|
||||
bool _handleKeyEvent(KeyEvent keyEvent) {
|
||||
// Logger.instance.debug(const UuidV4().generate(), "Hotkey: ${keyEvent}");
|
||||
if (_isFocused == false) return false;
|
||||
if (keyEvent is KeyUpEvent) return false;
|
||||
|
||||
// backspace to clear hotkey
|
||||
if (keyEvent.logicalKey == LogicalKeyboardKey.backspace) {
|
||||
|
@ -151,20 +54,12 @@ class _WoxHotkeyRecorderState extends State<WoxHotkeyRecorder> {
|
|||
return true;
|
||||
}
|
||||
|
||||
final physicalKeysPressed = HardwareKeyboard.instance.physicalKeysPressed;
|
||||
var modifiers = HotKeyModifier.values.where((e) => e.physicalKeys.any(physicalKeysPressed.contains)).toList();
|
||||
PhysicalKeyboardKey? key;
|
||||
physicalKeysPressed.removeWhere((element) => !isAllowedKey(element));
|
||||
if (physicalKeysPressed.isNotEmpty) {
|
||||
key = physicalKeysPressed.last;
|
||||
}
|
||||
|
||||
if (modifiers.isEmpty || key == null) {
|
||||
var newHotkey = WoxHotkey.parseHotkeyFromEvent(keyEvent);
|
||||
if (newHotkey == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var newHotkey = HotKey(key: key, modifiers: modifiers, scope: HotKeyScope.system);
|
||||
var hotkeyStr = getHotkeyString(newHotkey);
|
||||
var hotkeyStr = WoxHotkey.toStr(newHotkey);
|
||||
Logger.instance.debug(const UuidV4().generate(), "Hotkey str: $hotkeyStr");
|
||||
WoxApi.instance.isHotkeyAvailable(hotkeyStr).then((isAvailable) {
|
||||
Logger.instance.debug(const UuidV4().generate(), "Hotkey available: $isAvailable");
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
|
||||
class WoxHotkeyView extends StatelessWidget {
|
||||
final HotKey hotkey;
|
||||
|
||||
const WoxHotkeyView({super.key, required this.hotkey});
|
||||
|
||||
Widget buildSingleView(String key) {
|
||||
return Container(
|
||||
constraints: BoxConstraints.tight(const Size(24, 24)),
|
||||
decoration: BoxDecoration(color: Colors.grey[200], border: Border.all(color: Colors.grey[400]!), borderRadius: BorderRadius.circular(5)),
|
||||
child: Center(
|
||||
child: Text(
|
||||
key,
|
||||
style: const TextStyle(fontSize: 10),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String getModifierName(HotKeyModifier modifier) {
|
||||
if (modifier == HotKeyModifier.meta) {
|
||||
return "⌘";
|
||||
} else if (modifier == HotKeyModifier.alt) {
|
||||
return "⌥";
|
||||
} else if (modifier == HotKeyModifier.control) {
|
||||
return "⌃";
|
||||
} else if (modifier == HotKeyModifier.shift) {
|
||||
return "⇧";
|
||||
}
|
||||
|
||||
return modifier.name;
|
||||
}
|
||||
|
||||
String getKeyName(KeyboardKey key) {
|
||||
if (key == LogicalKeyboardKey.enter) {
|
||||
return "⏎";
|
||||
} else if (key == LogicalKeyboardKey.escape) {
|
||||
return "⎋";
|
||||
} else if (key == LogicalKeyboardKey.backspace) {
|
||||
return "⌫";
|
||||
} else if (key == LogicalKeyboardKey.delete) {
|
||||
return "⌦";
|
||||
} else if (key == LogicalKeyboardKey.arrowUp) {
|
||||
return "↑";
|
||||
} else if (key == LogicalKeyboardKey.arrowDown) {
|
||||
return "↓";
|
||||
} else if (key == LogicalKeyboardKey.arrowLeft) {
|
||||
return "←";
|
||||
} else if (key == LogicalKeyboardKey.arrowRight) {
|
||||
return "→";
|
||||
} else if (key == LogicalKeyboardKey.pageUp) {
|
||||
return "⇞";
|
||||
} else if (key == LogicalKeyboardKey.pageDown) {
|
||||
return "⇟";
|
||||
} else if (key == LogicalKeyboardKey.home) {
|
||||
return "↖";
|
||||
} else if (key == LogicalKeyboardKey.end) {
|
||||
return "↘";
|
||||
} else if (key == LogicalKeyboardKey.tab) {
|
||||
return "⇥";
|
||||
} else if (key == LogicalKeyboardKey.capsLock) {
|
||||
return "⇪";
|
||||
} else if (key == LogicalKeyboardKey.insert) {
|
||||
return "⌅";
|
||||
} else if (key == LogicalKeyboardKey.numLock) {
|
||||
return "⇭";
|
||||
} else if (key == LogicalKeyboardKey.scrollLock) {
|
||||
return "⇳";
|
||||
} else if (key == LogicalKeyboardKey.pause) {
|
||||
return "⎉";
|
||||
} else if (key == LogicalKeyboardKey.printScreen) {
|
||||
return "⎙";
|
||||
} else if (key == LogicalKeyboardKey.f1) {
|
||||
return "F1";
|
||||
} else if (key == LogicalKeyboardKey.f2) {
|
||||
return "F2";
|
||||
} else if (key == LogicalKeyboardKey.f3) {
|
||||
return "F3";
|
||||
} else if (key == LogicalKeyboardKey.f4) {
|
||||
return "F4";
|
||||
} else if (key == LogicalKeyboardKey.f5) {
|
||||
return "F5";
|
||||
}
|
||||
|
||||
return key.keyLabel;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var hotkeyWidgets = <Widget>[];
|
||||
if (hotkey.modifiers != null) {
|
||||
hotkeyWidgets.addAll(hotkey.modifiers!.map((o) => buildSingleView(getModifierName(o))));
|
||||
}
|
||||
hotkeyWidgets.add(buildSingleView(getKeyName(hotkey.key)));
|
||||
|
||||
return Row(children: [
|
||||
for (final widget in hotkeyWidgets)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: widget,
|
||||
)
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:from_css_color/from_css_color.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:uuid/v4.dart';
|
||||
import 'package:wox/components/wox_image_view.dart';
|
||||
import 'package:wox/entity/wox_image.dart';
|
||||
|
@ -12,6 +13,8 @@ import 'package:wox/enums/wox_result_tail_type_enum.dart';
|
|||
import 'package:wox/utils/log.dart';
|
||||
import 'package:wox/utils/wox_setting_util.dart';
|
||||
|
||||
import 'wox_hotkey_view.dart';
|
||||
|
||||
class WoxListItemView extends StatelessWidget {
|
||||
final bool isActive;
|
||||
final Rx<WoxImage> icon;
|
||||
|
@ -46,6 +49,53 @@ class WoxListItemView extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
Widget buildTails() {
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: WoxSettingUtil.instance.currentSetting.appWidth / 2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0, right: 5.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
for (final tail in tails)
|
||||
if (tail.type == WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_TEXT.code && tail.text != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: Text(
|
||||
tail.text!,
|
||||
style: TextStyle(
|
||||
color: fromCssColor(isActive ? woxTheme.resultItemActiveTailTextColor : woxTheme.resultItemTailTextColor),
|
||||
fontSize: 12,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
strutStyle: const StrutStyle(
|
||||
forceStrutHeight: true,
|
||||
),
|
||||
),
|
||||
)
|
||||
else if (tail.type == WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_HOTKEY.code && tail.hotkey != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: WoxHotkeyView(hotkey: tail.hotkey!),
|
||||
)
|
||||
else if (tail.type == WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_IMAGE.code && tail.image != null && tail.image!.imageData.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: WoxImageView(
|
||||
woxImage: tail.image!,
|
||||
width: getImageSize(tail.image!, 24),
|
||||
height: getImageSize(tail.image!, 24),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (LoggerSwitch.enablePaintLog) Logger.instance.info(const UuidV4().generate(), "repaint: list item view $key - container");
|
||||
|
@ -131,47 +181,8 @@ class WoxListItemView extends StatelessWidget {
|
|||
// Tails
|
||||
Obx(() {
|
||||
if (LoggerSwitch.enablePaintLog) Logger.instance.info(const UuidV4().generate(), "repaint: list item view $key - tails");
|
||||
|
||||
if (tails.isNotEmpty) {
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: WoxSettingUtil.instance.currentSetting.appWidth / 2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0, right: 5.0),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: [
|
||||
for (final tail in tails)
|
||||
if (tail.type == WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_TEXT.code && tail.text.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: Text(
|
||||
tail.text,
|
||||
style: TextStyle(
|
||||
color: fromCssColor(isActive ? woxTheme.resultItemActiveTailTextColor : woxTheme.resultItemTailTextColor),
|
||||
fontSize: 12,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
strutStyle: const StrutStyle(
|
||||
forceStrutHeight: true,
|
||||
),
|
||||
),
|
||||
)
|
||||
else if (tail.type == WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_IMAGE.code && tail.image.imageData.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: WoxImageView(
|
||||
woxImage: tail.image,
|
||||
width: getImageSize(tail.image, 24),
|
||||
height: getImageSize(tail.image, 24),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return buildTails();
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
|
||||
/// A hotkey in Wox at least consists of a modifier and a key.
|
||||
class WoxHotkey {
|
||||
static HotKey? parseHotkey(String value) {
|
||||
static HotKey? parseHotkeyFromString(String value) {
|
||||
final modifiers = <HotKeyModifier>[];
|
||||
LogicalKeyboardKey? key;
|
||||
value.split("+").forEach((element) {
|
||||
|
@ -166,13 +169,182 @@ class WoxHotkey {
|
|||
}
|
||||
});
|
||||
|
||||
if (modifiers.isEmpty || key == null) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return HotKey(
|
||||
key: key!,
|
||||
modifiers: modifiers,
|
||||
modifiers: modifiers.isEmpty ? null : modifiers,
|
||||
);
|
||||
}
|
||||
|
||||
static HotKey? parseHotkeyFromEvent(KeyEvent event) {
|
||||
if (event is KeyUpEvent) return null;
|
||||
|
||||
if (!WoxHotkey.isAllowedKey(event.physicalKey)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<HotKeyModifier> modifiers = [];
|
||||
if (HardwareKeyboard.instance.isAltPressed) {
|
||||
modifiers.add(HotKeyModifier.alt);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isControlPressed) {
|
||||
modifiers.add(HotKeyModifier.control);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isShiftPressed) {
|
||||
modifiers.add(HotKeyModifier.shift);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isMetaPressed) {
|
||||
modifiers.add(HotKeyModifier.meta);
|
||||
}
|
||||
|
||||
if (modifiers.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return HotKey(key: event.physicalKey, modifiers: modifiers, scope: HotKeyScope.system);
|
||||
}
|
||||
|
||||
static bool isAnyModifierPressed() {
|
||||
return HardwareKeyboard.instance.physicalKeysPressed.any((element) => HotKeyModifier.values.any((e) => e.physicalKeys.contains(element)));
|
||||
}
|
||||
|
||||
static List<HotKeyModifier> getPressedModifiers() {
|
||||
final modifiers = <HotKeyModifier>[];
|
||||
if (HardwareKeyboard.instance.isAltPressed) {
|
||||
modifiers.add(HotKeyModifier.alt);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isControlPressed) {
|
||||
modifiers.add(HotKeyModifier.control);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isShiftPressed) {
|
||||
modifiers.add(HotKeyModifier.shift);
|
||||
}
|
||||
if (HardwareKeyboard.instance.isMetaPressed) {
|
||||
modifiers.add(HotKeyModifier.meta);
|
||||
}
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
static bool isAllowedKey(PhysicalKeyboardKey key) {
|
||||
var allowedKeys = [
|
||||
PhysicalKeyboardKey.keyA,
|
||||
PhysicalKeyboardKey.keyB,
|
||||
PhysicalKeyboardKey.keyC,
|
||||
PhysicalKeyboardKey.keyD,
|
||||
PhysicalKeyboardKey.keyE,
|
||||
PhysicalKeyboardKey.keyF,
|
||||
PhysicalKeyboardKey.keyG,
|
||||
PhysicalKeyboardKey.keyH,
|
||||
PhysicalKeyboardKey.keyI,
|
||||
PhysicalKeyboardKey.keyJ,
|
||||
PhysicalKeyboardKey.keyK,
|
||||
PhysicalKeyboardKey.keyL,
|
||||
PhysicalKeyboardKey.keyM,
|
||||
PhysicalKeyboardKey.keyN,
|
||||
PhysicalKeyboardKey.keyO,
|
||||
PhysicalKeyboardKey.keyP,
|
||||
PhysicalKeyboardKey.keyQ,
|
||||
PhysicalKeyboardKey.keyR,
|
||||
PhysicalKeyboardKey.keyS,
|
||||
PhysicalKeyboardKey.keyT,
|
||||
PhysicalKeyboardKey.keyU,
|
||||
PhysicalKeyboardKey.keyV,
|
||||
PhysicalKeyboardKey.keyW,
|
||||
PhysicalKeyboardKey.keyX,
|
||||
PhysicalKeyboardKey.keyY,
|
||||
PhysicalKeyboardKey.keyZ,
|
||||
PhysicalKeyboardKey.digit1,
|
||||
PhysicalKeyboardKey.digit2,
|
||||
PhysicalKeyboardKey.digit3,
|
||||
PhysicalKeyboardKey.digit4,
|
||||
PhysicalKeyboardKey.digit5,
|
||||
PhysicalKeyboardKey.digit6,
|
||||
PhysicalKeyboardKey.digit7,
|
||||
PhysicalKeyboardKey.digit8,
|
||||
PhysicalKeyboardKey.digit9,
|
||||
PhysicalKeyboardKey.digit0,
|
||||
PhysicalKeyboardKey.space,
|
||||
PhysicalKeyboardKey.enter,
|
||||
PhysicalKeyboardKey.backspace,
|
||||
PhysicalKeyboardKey.delete,
|
||||
PhysicalKeyboardKey.arrowLeft,
|
||||
PhysicalKeyboardKey.arrowDown,
|
||||
PhysicalKeyboardKey.arrowRight,
|
||||
PhysicalKeyboardKey.arrowUp,
|
||||
];
|
||||
|
||||
return allowedKeys.contains(key);
|
||||
}
|
||||
|
||||
static bool equals(HotKey? a, HotKey? b) {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return a.key.keyLabel == b.key.keyLabel && isModifiersEquals(a.modifiers, b.modifiers);
|
||||
}
|
||||
|
||||
static bool isModifiersEquals(List<HotKeyModifier>? a, List<HotKeyModifier>? b) {
|
||||
if (a == null || b == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.length != b.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if all elements in a are in b
|
||||
// and all elements in b are in a
|
||||
return a.every((element) => b.map((o) => o.name).contains(element.name)) && b.every((element) => a.map((o) => o.name).contains(element.name));
|
||||
}
|
||||
|
||||
static String toStr(HotKey hotKey) {
|
||||
var modifiers = [];
|
||||
if (hotKey.modifiers != null) {
|
||||
for (var modifier in hotKey.modifiers!) {
|
||||
if (modifier == HotKeyModifier.shift) {
|
||||
modifiers.add("shift");
|
||||
} else if (modifier == HotKeyModifier.control) {
|
||||
modifiers.add("ctrl");
|
||||
} else if (modifier == HotKeyModifier.alt) {
|
||||
if (Platform.isMacOS) {
|
||||
modifiers.add("option");
|
||||
} else {
|
||||
modifiers.add("alt");
|
||||
}
|
||||
} else if (modifier == HotKeyModifier.meta) {
|
||||
if (Platform.isMacOS) {
|
||||
modifiers.add("cmd");
|
||||
} else {
|
||||
modifiers.add("win");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyStr = hotKey.key.keyLabel.toLowerCase();
|
||||
if (hotKey.key == PhysicalKeyboardKey.space) {
|
||||
keyStr = "space";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.enter) {
|
||||
keyStr = "enter";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.backspace) {
|
||||
keyStr = "backspace";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.delete) {
|
||||
keyStr = "delete";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowLeft) {
|
||||
keyStr = "left";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowDown) {
|
||||
keyStr = "down";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowRight) {
|
||||
keyStr = "right";
|
||||
} else if (hotKey.key == PhysicalKeyboardKey.arrowUp) {
|
||||
keyStr = "up";
|
||||
}
|
||||
|
||||
return "${modifiers.join("+")}+$keyStr";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import 'package:get/get.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/entity/wox_image.dart';
|
||||
import 'package:wox/entity/wox_preview.dart';
|
||||
import 'package:wox/enums/wox_last_query_mode_enum.dart';
|
||||
import 'package:wox/enums/wox_position_type_enum.dart';
|
||||
import 'package:wox/enums/wox_query_type_enum.dart';
|
||||
import 'package:wox/enums/wox_result_tail_type_enum.dart';
|
||||
import 'package:wox/enums/wox_selection_type_enum.dart';
|
||||
|
||||
class PlainQuery {
|
||||
|
@ -188,24 +191,68 @@ class WoxQueryResult {
|
|||
|
||||
class WoxQueryResultTail {
|
||||
late String type;
|
||||
late String text;
|
||||
late WoxImage image;
|
||||
late String? text;
|
||||
late WoxImage? image;
|
||||
late HotKey? hotkey;
|
||||
|
||||
WoxQueryResultTail({required this.type, required this.text, required this.image});
|
||||
WoxQueryResultTail({required this.type, this.text, this.image, this.hotkey});
|
||||
|
||||
WoxQueryResultTail.fromJson(Map<String, dynamic> json) {
|
||||
type = json['Type'];
|
||||
text = json['Text'];
|
||||
image = WoxImage.fromJson(json['Image']);
|
||||
if (json['Text'] != null) {
|
||||
text = json['Text'];
|
||||
} else {
|
||||
text = null;
|
||||
}
|
||||
|
||||
if (json['Image'] != null) {
|
||||
image = WoxImage.fromJson(json['Image']);
|
||||
} else {
|
||||
image = null;
|
||||
}
|
||||
|
||||
if (json['Hotkey'] != null) {
|
||||
hotkey = WoxHotkey.parseHotkeyFromString(json['Hotkey']);
|
||||
} else {
|
||||
hotkey = null;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = <String, dynamic>{};
|
||||
data['Type'] = type;
|
||||
data['Text'] = text;
|
||||
data['Image'] = image.toJson();
|
||||
|
||||
if (text != null) {
|
||||
data['Text'] = text;
|
||||
} else {
|
||||
data['Text'] = null;
|
||||
}
|
||||
|
||||
if (image != null) {
|
||||
data['Image'] = image!.toJson();
|
||||
} else {
|
||||
data['Image'] = null;
|
||||
}
|
||||
|
||||
if (hotkey != null) {
|
||||
data['Hotkey'] = hotkey!.toString();
|
||||
} else {
|
||||
data['Hotkey'] = null;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
factory WoxQueryResultTail.text(String text) {
|
||||
return WoxQueryResultTail(type: WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_TEXT.code, text: text);
|
||||
}
|
||||
|
||||
factory WoxQueryResultTail.hotkey(HotKey hotkey) {
|
||||
return WoxQueryResultTail(type: WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_HOTKEY.code, hotkey: hotkey);
|
||||
}
|
||||
|
||||
factory WoxQueryResultTail.image(WoxImage image) {
|
||||
return WoxQueryResultTail(type: WoxQueryResultTailTypeEnum.WOX_QUERY_RESULT_TAIL_TYPE_IMAGE.code, image: image);
|
||||
}
|
||||
}
|
||||
|
||||
class WoxResultAction {
|
||||
|
@ -214,8 +261,9 @@ class WoxResultAction {
|
|||
late Rx<WoxImage> icon;
|
||||
late bool isDefault;
|
||||
late bool preventHideAfterAction;
|
||||
late String hotkey;
|
||||
|
||||
WoxResultAction({required this.id, required this.name, required this.icon, required this.isDefault, required this.preventHideAfterAction});
|
||||
WoxResultAction({required this.id, required this.name, required this.icon, required this.isDefault, required this.preventHideAfterAction, required this.hotkey});
|
||||
|
||||
WoxResultAction.fromJson(Map<String, dynamic> json) {
|
||||
id = json['Id'];
|
||||
|
@ -223,6 +271,9 @@ class WoxResultAction {
|
|||
icon = (json['Icon'] != null ? WoxImage.fromJson(json['Icon']).obs : null)!;
|
||||
isDefault = json['IsDefault'];
|
||||
preventHideAfterAction = json['PreventHideAfterAction'];
|
||||
if (json['Hotkey'] != null) {
|
||||
hotkey = json['Hotkey'];
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
@ -232,11 +283,12 @@ class WoxResultAction {
|
|||
data['Icon'] = icon.toJson();
|
||||
data['IsDefault'] = isDefault;
|
||||
data['PreventHideAfterAction'] = preventHideAfterAction;
|
||||
data['Hotkey'] = hotkey;
|
||||
return data;
|
||||
}
|
||||
|
||||
static WoxResultAction empty() {
|
||||
return WoxResultAction(id: "", name: "".obs, icon: WoxImage.empty().obs, isDefault: false, preventHideAfterAction: false);
|
||||
return WoxResultAction(id: "", name: "".obs, icon: WoxImage.empty().obs, isDefault: false, preventHideAfterAction: false, hotkey: "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ typedef WoxQueryResultTailType = String;
|
|||
|
||||
enum WoxQueryResultTailTypeEnum {
|
||||
WOX_QUERY_RESULT_TAIL_TYPE_TEXT("text", "text"),
|
||||
WOX_QUERY_RESULT_TAIL_TYPE_IMAGE("image", "image");
|
||||
WOX_QUERY_RESULT_TAIL_TYPE_IMAGE("image", "image"),
|
||||
WOX_QUERY_RESULT_TAIL_TYPE_HOTKEY("hotkey", "hotkey");
|
||||
|
||||
final String code;
|
||||
final String value;
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||
import 'package:uuid/v4.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:wox/components/wox_image_view.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/modules/launcher/wox_launcher_controller.dart';
|
||||
import 'package:wox/utils/log.dart';
|
||||
|
||||
|
@ -21,46 +22,63 @@ class WoxQueryBoxView extends GetView<WoxLauncherController> {
|
|||
child: Focus(
|
||||
autofocus: true,
|
||||
onKeyEvent: (FocusNode node, KeyEvent event) {
|
||||
if (event is KeyDownEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.escape:
|
||||
controller.hideApp(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.handleQueryBoxArrowDown();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.handleQueryBoxArrowUp();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.enter:
|
||||
controller.executeAction(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.tab:
|
||||
controller.autoCompleteQuery(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.home:
|
||||
controller.moveQueryBoxCursorToStart();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.end:
|
||||
controller.moveQueryBoxCursorToEnd();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.keyJ:
|
||||
if (HardwareKeyboard.instance.isMetaPressed || HardwareKeyboard.instance.isAltPressed) {
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
var isAnyModifierPressed = WoxHotkey.isAnyModifierPressed();
|
||||
if (!isAnyModifierPressed) {
|
||||
if (event is KeyDownEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.escape:
|
||||
controller.hideApp(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.handleQueryBoxArrowDown();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.handleQueryBoxArrowUp();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.enter:
|
||||
controller.executeActiveAction(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.tab:
|
||||
controller.autoCompleteQuery(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.home:
|
||||
controller.moveQueryBoxCursorToStart();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.end:
|
||||
controller.moveQueryBoxCursorToEnd();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
|
||||
if (event is KeyRepeatEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.handleQueryBoxArrowDown();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.handleQueryBoxArrowUp();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event is KeyRepeatEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.handleQueryBoxArrowDown();
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.handleQueryBoxArrowUp();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
var pressedHotkey = WoxHotkey.parseHotkeyFromEvent(event);
|
||||
if (pressedHotkey == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// list all actions
|
||||
if (WoxHotkey.equals(pressedHotkey, WoxHotkey.parseHotkeyFromString("cmd+J"))) {
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
// check if the pressed hotkey is the action hotkey
|
||||
var result = controller.getActiveResult();
|
||||
var action = controller.getActionByHotkey(result, pressedHotkey);
|
||||
if (action != null) {
|
||||
controller.executeAction(const UuidV4().generate(), result, action);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
return KeyEventResult.ignored;
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:get/get.dart';
|
|||
import 'package:uuid/v4.dart';
|
||||
import 'package:wox/components/wox_list_item_view.dart';
|
||||
import 'package:wox/components/wox_preview_view.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/entity/wox_query.dart';
|
||||
import 'package:wox/enums/wox_direction_enum.dart';
|
||||
import 'package:wox/enums/wox_event_device_type_enum.dart';
|
||||
|
@ -18,6 +19,17 @@ import '../wox_launcher_controller.dart';
|
|||
class WoxQueryResultView extends GetView<WoxLauncherController> {
|
||||
const WoxQueryResultView({super.key});
|
||||
|
||||
RxList<WoxQueryResultTail> getHotkeyTails(WoxResultAction action) {
|
||||
var tails = <WoxQueryResultTail>[];
|
||||
if (action.hotkey != "") {
|
||||
var hotkey = WoxHotkey.parseHotkeyFromString(action.hotkey);
|
||||
if (hotkey != null) {
|
||||
tails.add(WoxQueryResultTail.hotkey(hotkey));
|
||||
}
|
||||
}
|
||||
return tails.obs;
|
||||
}
|
||||
|
||||
Widget getActionListView() {
|
||||
return Obx(() {
|
||||
if (LoggerSwitch.enablePaintLog) Logger.instance.info(const UuidV4().generate(), "repaint: action list view container");
|
||||
|
@ -40,7 +52,7 @@ class WoxQueryResultView extends GetView<WoxLauncherController> {
|
|||
woxTheme: controller.woxTheme.value,
|
||||
icon: woxResultAction.icon,
|
||||
title: woxResultAction.name,
|
||||
tails: RxList<WoxQueryResultTail>(),
|
||||
tails: getHotkeyTails(woxResultAction),
|
||||
subTitle: "".obs,
|
||||
isActive: controller.isActionActiveByIndex(index),
|
||||
listViewType: WoxListViewTypeEnum.WOX_LIST_VIEW_TYPE_ACTION.code,
|
||||
|
@ -170,42 +182,58 @@ class WoxQueryResultView extends GetView<WoxLauncherController> {
|
|||
Widget getActionQueryBox() {
|
||||
return Focus(
|
||||
onKeyEvent: (FocusNode node, KeyEvent event) {
|
||||
if (event is KeyDownEvent) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.escape) {
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
var isAnyModifierPressed = WoxHotkey.isAnyModifierPressed();
|
||||
if (!isAnyModifierPressed) {
|
||||
if (event is KeyDownEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.escape:
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_DOWN.code);
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_UP.code);
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.enter:
|
||||
controller.executeActiveAction(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_DOWN.code);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_UP.code);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if (event.logicalKey == LogicalKeyboardKey.enter) {
|
||||
controller.executeAction(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if ((HardwareKeyboard.instance.isMetaPressed || HardwareKeyboard.instance.isAltPressed) && event.logicalKey == LogicalKeyboardKey.keyJ) {
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
|
||||
if (event is KeyRepeatEvent) {
|
||||
switch (event.logicalKey) {
|
||||
case LogicalKeyboardKey.arrowDown:
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_DOWN.code);
|
||||
return KeyEventResult.handled;
|
||||
case LogicalKeyboardKey.arrowUp:
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_UP.code);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event is KeyRepeatEvent) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_DOWN.code);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||
controller.changeActionScrollPosition(
|
||||
const UuidV4().generate(), WoxEventDeviceTypeEnum.WOX_EVENT_DEVEICE_TYPE_KEYBOARD.code, WoxDirectionEnum.WOX_DIRECTION_UP.code);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
var pressedHotkey = WoxHotkey.parseHotkeyFromEvent(event);
|
||||
if (pressedHotkey == null) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// list all actions
|
||||
if (WoxHotkey.equals(pressedHotkey, WoxHotkey.parseHotkeyFromString("cmd+J"))) {
|
||||
controller.toggleActionPanel(const UuidV4().generate());
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
// check if the pressed hotkey is the action hotkey
|
||||
var result = controller.getActiveResult();
|
||||
var action = controller.getActionByHotkey(result, pressedHotkey);
|
||||
if (action != null) {
|
||||
controller.executeAction(const UuidV4().generate(), result, action);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
return KeyEventResult.ignored;
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:from_css_color/from_css_color.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:wox/components/wox_image_view.dart';
|
||||
import 'package:wox/entity/wox_image.dart';
|
||||
import 'package:wox/enums/wox_image_type_enum.dart';
|
||||
import 'package:wox/components/wox_hotkey_view.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/modules/launcher/wox_launcher_controller.dart';
|
||||
import 'package:wox/utils/wox_theme_util.dart';
|
||||
|
||||
|
@ -22,10 +19,11 @@ class WoxQueryToolbarView extends GetView<WoxLauncherController> {
|
|||
return const SizedBox();
|
||||
}
|
||||
|
||||
var hotkey = WoxHotkey.parseHotkeyFromString(action.hotkey) ?? WoxHotkey.parseHotkeyFromString("enter");
|
||||
return Row(
|
||||
children: [
|
||||
WoxImageView(woxImage: WoxImage(imageType: WoxImageTypeEnum.WOX_IMAGE_TYPE_EMOJI.code, imageData: "↩️")),
|
||||
Text(action.name.value, style: TextStyle(color: fromCssColor(controller.woxTheme.value.toolbarFontColor))),
|
||||
WoxHotkeyView(hotkey: hotkey!),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,10 +5,12 @@ import 'dart:ui';
|
|||
import 'package:desktop_drop/desktop_drop.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hotkey_manager/hotkey_manager.dart';
|
||||
import 'package:lpinyin/lpinyin.dart';
|
||||
import 'package:uuid/v4.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'package:wox/api/wox_api.dart';
|
||||
import 'package:wox/entity/wox_hotkey.dart';
|
||||
import 'package:wox/entity/wox_image.dart';
|
||||
import 'package:wox/entity/wox_preview.dart';
|
||||
import 'package:wox/entity/wox_query.dart';
|
||||
|
@ -236,17 +238,40 @@ class WoxLauncherController extends GetxController {
|
|||
return actions[activeActionIndex.value];
|
||||
}
|
||||
|
||||
Future<void> executeAction(String traceId) async {
|
||||
Logger.instance.debug(traceId, "user execute result action");
|
||||
/// given a hotkey, find the action in the result
|
||||
WoxResultAction? getActionByHotkey(WoxQueryResult? result, HotKey hotkey) {
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
WoxQueryResult? woxQueryResult = getActiveResult();
|
||||
if (woxQueryResult == null) {
|
||||
var filteredActions = result.actions.where((action) {
|
||||
var actionHotkey = WoxHotkey.parseHotkeyFromString(action.hotkey);
|
||||
if (WoxHotkey.equals(actionHotkey, hotkey)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (filteredActions.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return filteredActions.first;
|
||||
}
|
||||
|
||||
Future<void> executeActiveAction(String traceId) async {
|
||||
executeAction(traceId, getActiveResult(), getActiveAction());
|
||||
}
|
||||
|
||||
Future<void> executeAction(String traceId, WoxQueryResult? result, WoxResultAction? action) async {
|
||||
Logger.instance.debug(traceId, "user execute result action: ${action?.name}");
|
||||
|
||||
if (result == null) {
|
||||
Logger.instance.error(traceId, "active query result is null");
|
||||
return;
|
||||
}
|
||||
|
||||
WoxResultAction? activeAction = getActiveAction();
|
||||
if (activeAction == null) {
|
||||
if (action == null) {
|
||||
Logger.instance.error(traceId, "active action is null");
|
||||
return;
|
||||
}
|
||||
|
@ -257,12 +282,12 @@ class WoxLauncherController extends GetxController {
|
|||
type: WoxMsgTypeEnum.WOX_MSG_TYPE_REQUEST.code,
|
||||
method: WoxMsgMethodEnum.WOX_MSG_METHOD_ACTION.code,
|
||||
data: {
|
||||
"resultId": woxQueryResult.id,
|
||||
"actionId": activeAction.id,
|
||||
"resultId": result.id,
|
||||
"actionId": action.id,
|
||||
},
|
||||
));
|
||||
|
||||
if (!activeAction.preventHideAfterAction) {
|
||||
if (!action.preventHideAfterAction) {
|
||||
hideApp(traceId);
|
||||
}
|
||||
hideActionPanel();
|
||||
|
|
|
@ -72,7 +72,7 @@ class WoxSettingGeneralView extends GetView<WoxSettingController> {
|
|||
label: controller.tr("hotkey"),
|
||||
tips: controller.tr("hotkey_tips"),
|
||||
child: WoxHotkeyRecorder(
|
||||
hotkey: WoxHotkey.parseHotkey(controller.woxSetting.value.mainHotkey),
|
||||
hotkey: WoxHotkey.parseHotkeyFromString(controller.woxSetting.value.mainHotkey),
|
||||
onHotKeyRecorded: (hotkey) {
|
||||
controller.updateConfig("MainHotkey", hotkey);
|
||||
},
|
||||
|
@ -82,7 +82,7 @@ class WoxSettingGeneralView extends GetView<WoxSettingController> {
|
|||
label: controller.tr("selection_hotkey"),
|
||||
tips: controller.tr("selection_hotkey_tips"),
|
||||
child: WoxHotkeyRecorder(
|
||||
hotkey: WoxHotkey.parseHotkey(controller.woxSetting.value.selectionHotkey),
|
||||
hotkey: WoxHotkey.parseHotkeyFromString(controller.woxSetting.value.selectionHotkey),
|
||||
onHotKeyRecorded: (hotkey) {
|
||||
controller.updateConfig("SelectionHotkey", hotkey);
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.1.3 <4.0.0'
|
||||
sdk: '>=3.4.0 <4.0.0'
|
||||
|
||||
# Dependencies specify other packages that your package needs in order to work.
|
||||
# To automatically upgrade your package dependencies to the latest versions
|
||||
|
|
|
@ -503,6 +503,7 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu
|
|||
})
|
||||
if defaultActionCount == 0 && len(result.Actions) > 0 {
|
||||
result.Actions[0].IsDefault = true
|
||||
result.Actions[0].Hotkey = "Enter"
|
||||
}
|
||||
|
||||
var resultCache = &QueryResultCache{
|
||||
|
@ -516,8 +517,26 @@ func (m *Manager) PolishResult(ctx context.Context, pluginInstance *Instance, qu
|
|||
}
|
||||
|
||||
// store actions for ui invoke later
|
||||
for actionId := range result.Actions {
|
||||
var action = result.Actions[actionId]
|
||||
for actionIndex := range result.Actions {
|
||||
var action = result.Actions[actionIndex]
|
||||
|
||||
// if default action's hotkey is empty, set it as Enter
|
||||
if action.IsDefault && action.Hotkey == "" {
|
||||
result.Actions[actionIndex].Hotkey = "Enter"
|
||||
}
|
||||
|
||||
// replace hotkey modifiers for platform specific, E.g. replace win to cmd on macos, replace cmd to win on windows
|
||||
if util.IsMacOS() {
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "win", "cmd")
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "windows", "cmd")
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "alt", "option")
|
||||
}
|
||||
if util.IsWindows() || util.IsLinux() {
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "cmd", "win")
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "command", "win")
|
||||
result.Actions[actionIndex].Hotkey = strings.ReplaceAll(result.Actions[actionIndex].Hotkey, "option", "alt")
|
||||
}
|
||||
|
||||
if action.Action != nil {
|
||||
resultCache.Actions.Store(action.Id, action.Action)
|
||||
}
|
||||
|
|
|
@ -132,6 +132,10 @@ type QueryResultAction struct {
|
|||
// If true, Wox will not hide after user select this result
|
||||
PreventHideAfterAction bool
|
||||
Action func(ctx context.Context, actionContext ActionContext)
|
||||
// Hotkey to trigger this action. E.g. "ctrl+Shift+Space", "Ctrl+1", "Command+K"
|
||||
// Case insensitive, space insensitive
|
||||
// If IsDefault is true, Hotkey will be set to enter key by default
|
||||
Hotkey string
|
||||
}
|
||||
|
||||
type ActionContext struct {
|
||||
|
@ -158,6 +162,7 @@ func (q *QueryResult) ToUI() QueryResultUI {
|
|||
Icon: action.Icon,
|
||||
IsDefault: action.IsDefault,
|
||||
PreventHideAfterAction: action.PreventHideAfterAction,
|
||||
Hotkey: action.Hotkey,
|
||||
}
|
||||
}),
|
||||
RefreshInterval: q.RefreshInterval,
|
||||
|
@ -186,6 +191,7 @@ type QueryResultActionUI struct {
|
|||
Icon WoxImage
|
||||
IsDefault bool
|
||||
PreventHideAfterAction bool
|
||||
Hotkey string
|
||||
}
|
||||
|
||||
// store latest result value after query/refresh, so we can retrieve data later in action/refresh
|
||||
|
|
Loading…
Reference in New Issue