Brings ~55 mod-exclusive files to the upstream-based mod/master-resync branch: Activities (migrated to new ActivityManager pattern): - Clock/Time: SetTimeActivity, SetTimezoneOffsetActivity, NtpSyncActivity - Dictionary: DictionaryDefinitionActivity, DictionarySuggestionsActivity, DictionaryWordSelectActivity, LookedUpWordsActivity - Bookmark: EpubReaderBookmarkSelectionActivity - Book management: BookManageMenuActivity, EndOfBookMenuActivity - OPDS: OpdsServerListActivity, OpdsSettingsActivity - Utility: DirectoryPickerActivity, NumericStepperActivity Utilities (unchanged): - BookManager, BookSettings, BookmarkStore, BootNtpSync - Dictionary, LookupHistory, TimeSync, OpdsServerStore Libraries: PlaceholderCover, TableData, ChapterXPathIndexer Scripts: inject_mod_version, generate_book_icon, preview_placeholder_cover Docs: KOReader sync XPath mapping Migration changes: - ActivityWithSubactivity -> Activity base class - Callback constructors -> finish()/setResult() pattern - enterNewActivity() -> startActivityForResult() - Activity::RenderLock&& -> RenderLock&& These files won't compile yet - they reference mod settings and I18n strings that will be added in subsequent phases. Made-with: Cursor
4.1 KiB
Dictionary Feature Bug Fixes (Round 2)
Date: 2026-02-12
Branch: mod/add-dictionary
Task
Fix three remaining bugs with the dictionary word lookup feature after initial implementation and first round of fixes.
Changes Made
1. Fix: Lookup only works for first word searched (Dictionary.cpp)
Root cause: The binary search used C++ operator<= (case-sensitive, pure strcmp order) for comparing index entries, but StarDict .idx files are sorted using stardict_strcmp — a two-level comparison that sorts case-insensitively first, then uses case-sensitive strcmp as a tiebreaker. This means entries like "Simple" and "simple" are adjacent, and uppercase/lowercase entries for the same letter are interleaved (e.g., "Silver", "silver", "Simple", "simple", "Simpson", "simpson").
With the wrong sort order, the binary search overshoots for many words: uppercase entries like "Simpson" are case-sensitively < "simple" (because 'S' < 's'), so lo moves past the segment actually containing "simple". The linear scan then starts too late and doesn't find the word. By coincidence, some words (like "professor") happen to land in correct segments while others (like "simple") don't.
Fix:
- Added
stardictCmp()(case-insensitive first, then case-sensitive tiebreaker) andasciiCaseCmp()helper functions - Binary search now uses
stardictCmp(key, word) <= 0instead ofkey <= word - Linear scan early termination now uses
stardictCmp(key, word) > 0instead ofkey > word - Exact match now uses
asciiCaseCmp(key, word) == 0(case-insensitive) sincecleanWordlowercases the search term but the dictionary may store entries in any case - Removed the two-pass search approach (no longer needed — single pass handles all casing)
Files: src/util/Dictionary.cpp
2. Fix: Unrendered glyphs in pronunciation guide (DictionaryDefinitionActivity.cpp)
Root cause: The dictionary stores IPA pronunciation as raw text between entries, e.g., /əmˈsɛlvz/ appearing between </html> and the next <p> tag. These IPA Extension Unicode characters (U+0250–U+02FF) are not in the e-ink display's bitmap font, rendering as empty boxes.
Fix: Added IPA detection in parseHtml(): when encountering / or [ delimiters, the parser looks ahead for a matching close delimiter within 80 characters. If the enclosed content contains any non-ASCII byte (> 0x7F), the entire section (including delimiters) is skipped. This removes IPA transcriptions like /ˈsɪmpəl/ and [hɜː(ɹ)b] while preserving legitimate ASCII bracket content like "[citation needed]" or "and/or".
Files: src/activities/reader/DictionaryDefinitionActivity.cpp
3. Fix: Thinner button hints with overlap detection (DictionaryWordSelectActivity)
Root cause: Button hints used GUI.drawButtonHints() which draws 40px-tall buttons, taking excessive space over the book page content. No overlap detection meant hints could obscure the selected word at the bottom of the screen.
Fix:
- Replaced
GUI.drawButtonHints()with a customdrawHints()method - Draws thinner hints (22px instead of 40px) using
drawRect+ small text - Converts the selected word's bounding box from the current orientation to portrait coordinates (handles portrait, inverted, landscape CW/CCW)
- Checks vertical and horizontal overlap between each of the 4 button hint areas and the selected word (including hyphenation continuations)
- Individual hints that overlap the cursor are hidden (white area cleared, no button drawn)
- Uses the theme's button positions
[58, 146, 254, 342]to match the physical button layout
Files: src/activities/reader/DictionaryWordSelectActivity.h, src/activities/reader/DictionaryWordSelectActivity.cpp
Follow-up Items
- The landscape coordinate conversions for overlap detection are best-guess transforms; if they're wrong for the specific device rotation mapping, they may need tuning after testing
- The IPA skip heuristic is conservative (only skips content with non-ASCII in
//[delimiters); some edge-case IPA content outside these delimiters would still show SMALL_FONT_IDis now included viafontIds.hin the word select activity