fix: preserve full language rows when filtering picker

This commit is contained in:
2026-10-18 18:13:56 +02:00
parent fa3591adf8
commit cead6d9382
3 changed files with 28 additions and 1 deletions
+2
View File
@@ -150,6 +150,8 @@ Press Command-P or click the provider selector to expand a searchable action-sty
The footer shows the current language pair (initially **English → Japanese**). Click it or open **Actions** with Command-K to change the source language, change the target language, or swap them. Source and target selection use Conjure's themed popup, not a native menu. Fuzzy-search language names (for example, `jpns` matches **Japanese**), scroll the full list, and select with Up/Down, Control-J/K, and Return or by clicking a language. A checkmark marks the current language; it is selected and scrolled into view when the picker opens. Escape, Back, Command-K, or clicking outside cancels the picker without changing languages or leaving Translate. Language choices are saved locally and apply to both providers. Changing languages clears the previous result without submitting another request. The input hint and output accessibility label follow the selected pair. Escape closes an open picker first and dismisses the window only when no picker is open. Back closes the picker first, then returns from the context step to editing the original text (discarding context), then returns to the launcher and restores your search. Command-W always dismisses the entire window.
The language picker resizes to complete rows as results are filtered, showing up to six rows before scrolling.
Editing the input, changing languages or providers, saving or forgetting the key in Settings, or leaving Translate cancels the active request; cancelled results are not shown. After a request fails, submit again to retry.
- **MyMemory:** No account or key required. Limits are 500 UTF-8 bytes per request and [5,000 characters per day](https://mymemory.translated.net/doc/usagelimits.php) for anonymous use.
@@ -43,6 +43,7 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
private var currentLanguage: String?
private var languageTable: NSTableView?
private var languageScroll: NSScrollView?
private var languageViewportHeight: NSLayoutConstraint?
var closed: (() -> Void)?
var isShown: Bool { viewIfLoaded?.superview != nil && !isClosing }
@@ -59,6 +60,7 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
rowTopConstraints = []
languageTable = nil
languageScroll = nil
languageViewportHeight = nil
entries = [
ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text),
ActionEntry(title: "Favorite", symbol: "star", color: SunsetPalette.text),
@@ -155,7 +157,9 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
])
content.layer?.borderColor = SunsetPalette.secondaryText.withAlphaComponent(0.22).cgColor
if languages != nil {
height.isActive = false
let table = NSTableView()
table.style = .plain
table.addTableColumn(NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Language")))
table.headerView = nil
table.backgroundColor = .clear
@@ -177,14 +181,17 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
scroll.drawsBackground = false
scroll.translatesAutoresizingMaskIntoConstraints = false
content.addSubview(scroll)
let viewportHeight = scroll.heightAnchor.constraint(equalToConstant: LauncherRowView.rowHeight)
NSLayoutConstraint.activate([
scroll.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8),
scroll.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8),
scroll.topAnchor.constraint(equalTo: applicationLabel.bottomAnchor, constant: 10),
scroll.bottomAnchor.constraint(equalTo: search.topAnchor, constant: -8),
viewportHeight,
])
languageTable = table
languageScroll = scroll
languageViewportHeight = viewportHeight
}
updateSelection()
}
@@ -345,9 +352,10 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
let selection = (query.isEmpty ? matchingLanguages.firstIndex(where: { $0.code == currentLanguage }) : nil)
?? (matchingLanguages.isEmpty ? nil : 0)
for entry in entries { entry.view.isHidden = true }
heightConstraint?.constant = 116 + CGFloat(max(min(matchingLanguages.count, 6), 1) - 1) * LauncherRowView.rowHeight
languageViewportHeight?.constant = CGFloat(max(min(matchingLanguages.count, 6), 1)) * table.rowHeight
empty.isHidden = !matchingLanguages.isEmpty
languageScroll?.isHidden = matchingLanguages.isEmpty
view.superview?.layoutSubtreeIfNeeded()
table.reloadData()
selectedEntry = selection
if let selectedEntry {
@@ -100,5 +100,22 @@ struct LanguagePickerTests {
#expect(selected == "5")
popup.close(animated: false)
#expect(!pill.isAccessibilityHidden())
popup.showLanguages(choices, selected: "15", source: true, relativeTo: anchor)
let search = try #require(descendants(popup.view).compactMap { $0 as? NSSearchField }.first)
let filteredTable = try #require(descendants(popup.view).compactMap { $0 as? NSTableView }.first)
// Shrink from a scrolled list to one result, then grow and shrink again.
for query in ["Language 19", "", "no matching language", "Language 03"] {
search.stringValue = query
search.delegate?.controlTextDidChange?(Notification(name: NSControl.textDidChangeNotification, object: search))
host.layoutSubtreeIfNeeded()
let count = filteredTable.numberOfRows
if count > 0 {
let row = filteredTable.rect(ofRow: filteredTable.selectedRow)
#expect(filteredTable.visibleRect.contains(row))
let viewport = try #require(filteredTable.enclosingScrollView)
#expect(viewport.contentSize.height >= min(CGFloat(count), 6) * filteredTable.rowHeight)
}
}
}
}