From 19ff2345331b9a3522321fff4400a26e0b068b88 Mon Sep 17 00:00:00 2001 From: Maksim Karelov Date: Mon, 9 Jan 2023 15:27:57 +0600 Subject: [PATCH] fix: reduce flickering when searching for results (#30) * fix: reduce flickering Related to #25. Search results flickers because of three things (IMO): - `handleInput` resets the `results` array, which immediately dropping list with `#if` directive in `SearchResult` component - Changing styles on `focus` and `blur` are not immediate operation, that's why another kind of flickering is a transition between focused and non-focused state (gray background) - `getIcon` function is another "bottleneck"; preservation of space for upcomming tag is a key for rescue Most "ugly" part of this code is manipulating with classes. Changing classes through Svelte's `class:` binding leads to focus lost. Unfortunately, manipulating classes through script cannot be made with obfuscated selectors, that's why you can see `:global` in code. Maybe you know better way to change classes without losing focus. * fix: span attributes * fix: remove icon flickering with icons cache * fix: reset `results` when search pattern is empty * chore: fix extra rectangular overlay on calculation results * chore: fix closing window when pressing enter/return * chore: fix keys not being registered Co-authored-by: Parth Jadhav --- src/cache.ts | 5 ++- src/routes/App/App.svelte | 2 +- src/routes/App/lib/CalculationResult.svelte | 3 +- src/routes/App/lib/SearchResult.svelte | 48 +++++++++++++++------ 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/src/cache.ts b/src/cache.ts index 681f4c9..bca192b 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -1,2 +1,5 @@ export const preferences = new Map(); -export const paths = new Map(); \ No newline at end of file +export const paths = new Map(); +export const icons = new Map(); + +export const FALLBACK_ICON_SYMBOL = Symbol(); diff --git a/src/routes/App/App.svelte b/src/routes/App/App.svelte index b0b9b50..27a0b6d 100644 --- a/src/routes/App/App.svelte +++ b/src/routes/App/App.svelte @@ -64,8 +64,8 @@ }); const handleInput = async (event: any) => { - results = []; if (event.target.value === "") { + results = []; footerText = "verve.app"; return; } diff --git a/src/routes/App/lib/CalculationResult.svelte b/src/routes/App/lib/CalculationResult.svelte index dd9631d..b04e92a 100644 --- a/src/routes/App/lib/CalculationResult.svelte +++ b/src/routes/App/lib/CalculationResult.svelte @@ -7,6 +7,7 @@ }; const searchResultClicked = async (event: any) => { + if (event.keyCode !== 13) return; await copyAnswer(); const searchBarInput = document.getElementById( "searchBarInput" @@ -18,7 +19,7 @@
-
diff --git a/src/routes/App/lib/SearchResult.svelte b/src/routes/App/lib/SearchResult.svelte index e9b6d05..365ee8c 100644 --- a/src/routes/App/lib/SearchResult.svelte +++ b/src/routes/App/lib/SearchResult.svelte @@ -6,6 +6,7 @@ import { convertFileSrc } from "@tauri-apps/api/tauri"; import { appWindow, LogicalSize } from "@tauri-apps/api/window"; import { afterUpdate } from "svelte"; + import { FALLBACK_ICON_SYMBOL, icons } from "../../../cache"; import CalculationResult from "./CalculationResult.svelte"; afterUpdate(async () => { @@ -13,14 +14,27 @@ await appWindow.setSize(new LogicalSize(750, height)); if (results.length > 0 && results[0] !== "") { const firstResult = document.getElementById(results[0]); + firstResult.classList.add('searchResultFocused'); await firstResult.focus(); } }); async function getIcon(app_name: string) { - const fallbackIcon = convertFileSrc( - await resolveResource("assets/default.svg") - ); + let icon = icons.get(app_name); + let fallbackIcon = icons.get(FALLBACK_ICON_SYMBOL); + + if (icon && fallbackIcon) { + return { icon, fallbackIcon }; + } + + if (!fallbackIcon) { + fallbackIcon = convertFileSrc( + await resolveResource("assets/default.svg") + ); + icons.set(FALLBACK_ICON_SYMBOL, fallbackIcon); + } + + let iconPath: string; if ( [ "Migration Assistant", @@ -31,14 +45,17 @@ "AirPort Utility", ].includes(app_name) ) { - const icon_path = await resolveResource( + iconPath = await resolveResource( `assets/appIcons/${app_name}.app.png` ); - return { icon: convertFileSrc(icon_path), fallbackIcon }; + } else { + const appDataDirPath = await appDataDir(); + iconPath = await join(appDataDirPath, `appIcons/${app_name}.app.png`); } - const appDataDirPath = await appDataDir(); - const filePath = await join(appDataDirPath, `appIcons/${app_name}.app.png`); - return { icon: convertFileSrc(filePath), fallbackIcon }; + + icon = convertFileSrc(iconPath); + icons.set(app_name, icon); + return { icon, fallbackIcon }; } async function handleKeydown(event) { @@ -58,6 +75,8 @@ else newIndex = (currentIndex + 1) % items.length; } if (current !== null && items[newIndex] !== null) { + items[newIndex].classList.add('searchResultFocused'); + current.classList.remove('searchResultFocused'); current.blur(); items[newIndex].focus(); } @@ -85,7 +104,9 @@ {#await getIcon(result .split("/") .pop() - .replace(/.app$/, "")) then { icon, fallbackIcon }} + .replace(/.app$/, ""))} + + {:then {icon, fallbackIcon}}