diff --git a/src-tauri/src/services/settings.rs b/src-tauri/src/services/settings.rs index 5acbe87..623d111 100644 --- a/src-tauri/src/services/settings.rs +++ b/src-tauri/src/services/settings.rs @@ -8,6 +8,7 @@ pub struct SettingsSpec { pub is_wizard_completed: bool, pub log_level: String, pub window_zoom: f64, + pub search_keyboard_layout: String, pub themes_custom: JsonValue, pub auto_score_enabled: bool, pub auto_score_rules: JsonValue, @@ -22,6 +23,7 @@ impl Default for SettingsSpec { is_wizard_completed: false, log_level: "info".to_string(), window_zoom: 1.0, + search_keyboard_layout: "qwerty26".to_string(), themes_custom: JsonValue::Array(vec![]), auto_score_enabled: false, auto_score_rules: JsonValue::Array(vec![]), @@ -37,6 +39,7 @@ pub enum SettingsKey { IsWizardCompleted, LogLevel, WindowZoom, + SearchKeyboardLayout, ThemesCustom, AutoScoreEnabled, AutoScoreRules, @@ -51,6 +54,7 @@ impl SettingsKey { SettingsKey::IsWizardCompleted => "is_wizard_completed", SettingsKey::LogLevel => "log_level", SettingsKey::WindowZoom => "window_zoom", + SettingsKey::SearchKeyboardLayout => "search_keyboard_layout", SettingsKey::ThemesCustom => "themes_custom", SettingsKey::AutoScoreEnabled => "auto_score_enabled", SettingsKey::AutoScoreRules => "auto_score_rules", @@ -65,6 +69,7 @@ impl SettingsKey { "is_wizard_completed" => Some(SettingsKey::IsWizardCompleted), "log_level" => Some(SettingsKey::LogLevel), "window_zoom" => Some(SettingsKey::WindowZoom), + "search_keyboard_layout" => Some(SettingsKey::SearchKeyboardLayout), "themes_custom" => Some(SettingsKey::ThemesCustom), "auto_score_enabled" => Some(SettingsKey::AutoScoreEnabled), "auto_score_rules" => Some(SettingsKey::AutoScoreRules), @@ -270,6 +275,22 @@ impl SettingsService { }, ); + defs.insert( + SettingsKey::SearchKeyboardLayout, + SettingDefinition { + kind: SettingValueKind::String, + default_value: SettingsValue::String("qwerty26".to_string()), + write_permission: PermissionRequirement::Admin, + validate: Some(|v| { + if let SettingsValue::String(s) = v { + matches!(s.as_str(), "t9" | "qwerty26") + } else { + false + } + }), + }, + ); + defs.insert( SettingsKey::ThemesCustom, SettingDefinition { @@ -434,6 +455,10 @@ impl SettingsService { SettingsValue::Number(n) => n, _ => 1.0, }, + search_keyboard_layout: match self.get_value(SettingsKey::SearchKeyboardLayout) { + SettingsValue::String(s) => s, + _ => "qwerty26".to_string(), + }, themes_custom: match self.get_value(SettingsKey::ThemesCustom) { SettingsValue::Json(j) => j, _ => JsonValue::Array(vec![]), diff --git a/src/components/Home.tsx b/src/components/Home.tsx index fbb7719..0692ed8 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -24,6 +24,7 @@ interface reason { } type SortType = "alphabet" | "surname" | "score" +type SearchKeyboardLayout = "t9" | "qwerty26" const T9_KEY_MAP: Record = { a: "2", @@ -62,6 +63,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { const [sortType, setSortType] = useState("alphabet") const [searchKeyword, setSearchKeyword] = useState("") const [showPinyinKeyboard, setShowPinyinKeyboard] = useState(false) + const [searchKeyboardLayout, setSearchKeyboardLayout] = useState("qwerty26") const scrollContainerRef = useRef(null) const groupRefs = useRef>({}) @@ -125,6 +127,46 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { return () => window.removeEventListener("ss:data-updated", onDataUpdated as any) }, [fetchData]) + useEffect(() => { + const api = (window as any).api + if (!api) return + + let disposed = false + let unlisten: (() => void) | null = null + + api + .getSetting("search_keyboard_layout") + .then((res: any) => { + if (disposed) return + const value = res?.data + if (value === "t9" || value === "qwerty26") { + setSearchKeyboardLayout(value) + } + }) + .catch(() => void 0) + + api + .onSettingChanged((change: any) => { + if (change?.key !== "search_keyboard_layout") return + if (change?.value === "t9" || change?.value === "qwerty26") { + setSearchKeyboardLayout(change.value) + } + }) + .then((fn: () => void) => { + if (disposed) { + fn() + return + } + unlisten = fn + }) + .catch(() => void 0) + + return () => { + disposed = true + if (unlisten) unlisten() + } + }, []) + useEffect(() => { const onDocumentClick = (e: MouseEvent) => { const target = e.target as Node | null @@ -136,7 +178,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { return () => document.removeEventListener("mousedown", onDocumentClick) }, []) - const pinyinKeyRows = [ + const t9KeyRows = [ [ { digit: "2", letters: "ABC" }, { digit: "3", letters: "DEF" }, @@ -154,6 +196,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { ], ] + const qwertyKeyRows = [ + ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"], + ["a", "s", "d", "f", "g", "h", "j", "k", "l"], + ["z", "x", "c", "v", "b", "n", "m", "⌫"], + ] + const toT9Digits = (text: string) => text .toLowerCase() @@ -161,12 +209,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { .map((ch) => T9_KEY_MAP[ch] || "") .join("") - const handlePinyinKeyPress = (digit: string) => { - if (digit === "⌫") { + const handleSearchKeyPress = (keyValue: string) => { + if (keyValue === "⌫") { setSearchKeyword((prev) => prev.slice(0, -1)) return } - setSearchKeyword((prev) => `${prev}${digit}`) + setSearchKeyword((prev) => `${prev}${keyValue}`) } const getDisplayText = (name: string) => { @@ -861,7 +909,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { position: "absolute", top: "calc(100% + 8px)", left: 0, - width: "220px", + width: searchKeyboardLayout === "qwerty26" ? "260px" : "220px", padding: "8px", borderRadius: "10px", border: "1px solid var(--ss-border-color)", @@ -871,23 +919,45 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => { }} >
- {pinyinKeyRows.map((row, rowIndex) => ( -
- {row.map((keyItem) => ( - + {row.map((keyItem) => ( + + ))} +
+ )) + : t9KeyRows.map((row, rowIndex) => ( +
+ {row.map((keyItem) => ( + + ))} +
))} -
- ))} )} diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 25f6ee7..cc6444c 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -9,6 +9,7 @@ type appSettings = { is_wizard_completed: boolean log_level: "debug" | "info" | "warn" | "error" window_zoom?: string + search_keyboard_layout?: "t9" | "qwerty26" auto_score_enabled?: boolean } @@ -20,6 +21,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission is_wizard_completed: false, log_level: "info", window_zoom: "1.0", + search_keyboard_layout: "qwerty26", }) const [securityStatus, setSecurityStatus] = useState<{ @@ -108,6 +110,8 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission if (change?.key === "is_wizard_completed") return { ...prev, is_wizard_completed: change.value } if (change?.key === "window_zoom") return { ...prev, window_zoom: change.value } + if (change?.key === "search_keyboard_layout") + return { ...prev, search_keyboard_layout: change.value } if (change?.key === "auto_score_enabled") return { ...prev, auto_score_enabled: change.value } return prev @@ -377,6 +381,34 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission
+ +