diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f4c37c..3c0df60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,23 @@ Rules: `Unreleased`. - Add the date and the compare link once the version tag exists. +## [Unreleased] + +### Added + +- The search shows which hit is selected and how many there are. + +### Changed + +- The engine is odrcore 7.6.0, up from 7.5.2. Long text files, PDFs, sheets + and presentations open faster. +- A cell of an array formula says why it cannot be edited. + +### Fixed + +- Words in a Word document no longer run together where the formatting changes. +- A font that is not installed falls back to one of the same kind. + ## [1.49] ### Added diff --git a/OpenDocumentReader.xcodeproj/project.pbxproj b/OpenDocumentReader.xcodeproj/project.pbxproj index c437981..9248775 100644 --- a/OpenDocumentReader.xcodeproj/project.pbxproj +++ b/OpenDocumentReader.xcodeproj/project.pbxproj @@ -830,7 +830,7 @@ repositoryURL = "https://github.com/opendocument-app/OpenDocument.core.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 7.5.2; + minimumVersion = 7.6.0; }; }; AD584FCD41577C8CDEE974AA /* XCRemoteSwiftPackageReference "swift-package-manager-google-mobile-ads" */ = { diff --git a/OpenDocumentReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/OpenDocumentReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index dd61562..1a09a57 100644 --- a/OpenDocumentReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/OpenDocumentReader.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -6,8 +6,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/opendocument-app/OpenDocument.core.git", "state" : { - "revision" : "77e2a0a6e4b33c33a5aee64551fcd0d7493c9101", - "version" : "7.5.2" + "revision" : "6fa7cb06fd885afa95240e2ac6b198becf46f740", + "version" : "7.6.0" } }, { diff --git a/OpenDocumentReader/DocumentViewController.swift b/OpenDocumentReader/DocumentViewController.swift index 2fec2f4..289fd7d 100644 --- a/OpenDocumentReader/DocumentViewController.swift +++ b/OpenDocumentReader/DocumentViewController.swift @@ -167,6 +167,10 @@ class DocumentViewController: UIViewController, DocumentDelegate, UISearchBarDel return slot }() + /// The place of the selected hit and the number of hits, below the search bar. + let searchCountLabel = UILabel() + private let searchCountPill = UIView() + private var searchBarHeightWhenShown: NSLayoutConstraint? private var searchBarHeightWhenHidden: NSLayoutConstraint? private lazy var pageTabBarHeight = pageTabBar.heightAnchor.constraint(equalToConstant: 0) @@ -197,6 +201,7 @@ class DocumentViewController: UIViewController, DocumentDelegate, UISearchBarDel searchBar.showsCancelButton = true searchBarHeightWhenShown = searchBar.heightAnchor.constraint(equalToConstant: 56) searchBarHeightWhenHidden = searchBar.heightAnchor.constraint(equalToConstant: 0) + setUpSearchCount() setUpEditToolBar() setUpPageMessages() @@ -744,6 +749,11 @@ class DocumentViewController: UIViewController, DocumentDelegate, UISearchBarDel } staleCells = count + case "searchChange": + showSearchCount( + detail["count"] as? Int ?? 0, current: detail["current"] as? Int ?? -1, + complete: detail["complete"] as? Bool ?? true) + case "editRefused": editRefused(reason: detail["reason"] as? String ?? "") @@ -965,6 +975,7 @@ class DocumentViewController: UIViewController, DocumentDelegate, UISearchBarDel case "newLine": key = "edit_refused_new_line" case "formula": key = "edit_refused_formula" case "formulaInput": key = "edit_refused_formula_input" + case "array": key = "edit_refused_array" case "rich", "shapes": key = "edit_refused_rich" case "readOnly": key = "edit_refused_read_only" case "range": key = "edit_refused_range" @@ -1150,9 +1161,46 @@ class DocumentViewController: UIViewController, DocumentDelegate, UISearchBarDel searchBar.becomeFirstResponder() } + private func setUpSearchCount() { + searchCountLabel.font = .monospacedDigitSystemFont(ofSize: UIFont.smallSystemFontSize, weight: .medium) + searchCountLabel.textColor = .secondaryLabel + searchCountLabel.translatesAutoresizingMaskIntoConstraints = false + + // over the document, because the search field does not show a right view + searchCountPill.backgroundColor = .secondarySystemBackground + searchCountPill.layer.cornerRadius = 12 + searchCountPill.isUserInteractionEnabled = false + searchCountPill.translatesAutoresizingMaskIntoConstraints = false + searchCountPill.addSubview(searchCountLabel) + view.addSubview(searchCountPill) + + NSLayoutConstraint.activate([ + searchCountLabel.leadingAnchor.constraint(equalTo: searchCountPill.leadingAnchor, constant: 10), + searchCountLabel.trailingAnchor.constraint(equalTo: searchCountPill.trailingAnchor, constant: -10), + searchCountLabel.topAnchor.constraint(equalTo: searchCountPill.topAnchor, constant: 4), + searchCountLabel.bottomAnchor.constraint(equalTo: searchCountPill.bottomAnchor, constant: -4), + searchCountPill.topAnchor.constraint(equalTo: searchBar.bottomAnchor, constant: 8), + searchCountPill.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16), + ]) + showSearchCount(0, current: -1, complete: true) + } + + /// `current` counts from 0, and is -1 where no hit is selected. Hits that + /// are still being counted show as a "+". + private func showSearchCount(_ count: Int, current: Int, complete: Bool) { + if searchBar.isHidden || searchBar.text?.isEmpty ?? true { + searchCountLabel.text = nil + } else { + let total = "\(count)\(complete ? "" : "+")" + searchCountLabel.text = current >= 0 ? "\(current + 1)/\(total)" : total + } + searchCountPill.isHidden = searchCountLabel.text == nil + } + private func hideSearchBar() { searchBar.text = "" searchBar.isHidden = true + showSearchCount(0, current: -1, complete: true) searchBarHeightWhenHidden?.isActive = true searchBarHeightWhenShown?.isActive = false diff --git a/OpenDocumentReader/ca.lproj/Localizable.strings b/OpenDocumentReader/ca.lproj/Localizable.strings index 3f52427..f20d449 100644 --- a/OpenDocumentReader/ca.lproj/Localizable.strings +++ b/OpenDocumentReader/ca.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Aquesta cel·la conté una fórmula i es queda com està."; "edit_refused_formula_input" = "Escriure una fórmula encara no és compatible."; +"edit_refused_array" = "Aquesta cel·la forma part d’una fórmula matricial i es queda com està."; "edit_refused_rich" = "Aquesta cel·la conté més que text simple i es queda com està."; "edit_refused_generic" = "Aquesta edició no és possible aquí."; "edit_refused_new_line" = "Un salt de línia dins d’un paràgraf no es pot desar. Prem Retorn per a un paràgraf nou."; diff --git a/OpenDocumentReader/cs.lproj/Localizable.strings b/OpenDocumentReader/cs.lproj/Localizable.strings index 285bdbb..9d08847 100644 --- a/OpenDocumentReader/cs.lproj/Localizable.strings +++ b/OpenDocumentReader/cs.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Tato buňka obsahuje vzorec a zůstává beze změny."; "edit_refused_formula_input" = "Zadávání vzorců zatím není podporováno."; +"edit_refused_array" = "Tato buňka je součástí maticového vzorce a zůstane beze změny."; "edit_refused_rich" = "Tato buňka obsahuje víc než prostý text a zůstává beze změny."; "edit_refused_generic" = "Tato úprava tu není možná."; "edit_refused_new_line" = "Zalomení řádku uvnitř odstavce nelze uložit. Stiskněte Enter pro nový odstavec."; diff --git a/OpenDocumentReader/da.lproj/Localizable.strings b/OpenDocumentReader/da.lproj/Localizable.strings index 5c836a3..59b6cfb 100644 --- a/OpenDocumentReader/da.lproj/Localizable.strings +++ b/OpenDocumentReader/da.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Den celle indeholder en formel og forbliver, som den er."; "edit_refused_formula_input" = "Indtastning af formler understøttes ikke endnu."; +"edit_refused_array" = "Cellen er en del af en matrixformel og forbliver uændret."; "edit_refused_rich" = "Den celle indeholder mere end ren tekst og forbliver, som den er."; "edit_refused_generic" = "Den redigering er ikke mulig her."; "edit_refused_new_line" = "Et linjeskift inde i et afsnit kan ikke gemmes. Tryk på Enter for et nyt afsnit."; diff --git a/OpenDocumentReader/de.lproj/Localizable.strings b/OpenDocumentReader/de.lproj/Localizable.strings index 7e663af..2bc6928 100644 --- a/OpenDocumentReader/de.lproj/Localizable.strings +++ b/OpenDocumentReader/de.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Diese Zelle enthält eine Formel und bleibt, wie sie ist."; "edit_refused_formula_input" = "Formeln eingeben wird noch nicht unterstützt."; +"edit_refused_array" = "Diese Zelle gehört zu einer Matrixformel und bleibt, wie sie ist."; "edit_refused_rich" = "Diese Zelle enthält mehr als reinen Text und bleibt, wie sie ist."; "edit_refused_generic" = "Diese Änderung ist hier nicht möglich."; "edit_refused_new_line" = "Ein Zeilenumbruch innerhalb eines Absatzes kann nicht gespeichert werden. Drücke die Eingabetaste für einen neuen Absatz."; diff --git a/OpenDocumentReader/en.lproj/Localizable.strings b/OpenDocumentReader/en.lproj/Localizable.strings index 7c7ffab..f124400 100644 --- a/OpenDocumentReader/en.lproj/Localizable.strings +++ b/OpenDocumentReader/en.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "That cell holds a formula and stays as it is."; "edit_refused_formula_input" = "Typing a formula is not supported yet."; +"edit_refused_array" = "That cell is part of an array formula and stays as it is."; "edit_refused_rich" = "That cell holds more than plain text and stays as it is."; "edit_refused_generic" = "That edit is not possible here."; "edit_refused_new_line" = "A line break inside a paragraph cannot be saved. Press Return for a new paragraph."; diff --git a/OpenDocumentReader/es.lproj/Localizable.strings b/OpenDocumentReader/es.lproj/Localizable.strings index 1fb0ff5..652defe 100644 --- a/OpenDocumentReader/es.lproj/Localizable.strings +++ b/OpenDocumentReader/es.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Esa celda contiene una fórmula y se queda como está."; "edit_refused_formula_input" = "Escribir una fórmula aún no es compatible."; +"edit_refused_array" = "Esa celda forma parte de una fórmula matricial y se queda como está."; "edit_refused_rich" = "Esa celda contiene más que texto sin formato y se queda como está."; "edit_refused_generic" = "Esa edición no es posible aquí."; "edit_refused_new_line" = "Un salto de línea dentro de un párrafo no se puede guardar. Pulsa Intro para un párrafo nuevo."; diff --git a/OpenDocumentReader/fr.lproj/Localizable.strings b/OpenDocumentReader/fr.lproj/Localizable.strings index b08735c..0c6b70c 100644 --- a/OpenDocumentReader/fr.lproj/Localizable.strings +++ b/OpenDocumentReader/fr.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Cette cellule contient une formule et reste telle quelle."; "edit_refused_formula_input" = "La saisie d’une formule n’est pas encore prise en charge."; +"edit_refused_array" = "Cette cellule fait partie d’une formule matricielle et reste telle quelle."; "edit_refused_rich" = "Cette cellule contient plus que du texte brut et reste telle quelle."; "edit_refused_generic" = "Cette modification n’est pas possible ici."; "edit_refused_new_line" = "Un saut de ligne à l’intérieur d’un paragraphe ne peut pas être enregistré. Appuyez sur Entrée pour un nouveau paragraphe."; diff --git a/OpenDocumentReader/ga.lproj/Localizable.strings b/OpenDocumentReader/ga.lproj/Localizable.strings index 2ce69e3..562e4ea 100644 --- a/OpenDocumentReader/ga.lproj/Localizable.strings +++ b/OpenDocumentReader/ga.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Tá foirmle sa chill sin agus fanann sí mar atá."; "edit_refused_formula_input" = "Ní thacaítear le foirmlí a chlóscríobh go fóill."; +"edit_refused_array" = "Is cuid d’fhoirmle eagair an cill sin agus fanann sí mar atá."; "edit_refused_rich" = "Tá níos mó ná gnáth-théacs sa chill sin agus fanann sí mar atá."; "edit_refused_generic" = "Ní féidir an t-athrú sin a dhéanamh anseo."; "edit_refused_new_line" = "Ní féidir briseadh líne laistigh d’alt a shábháil. Brúigh Iontráil le haghaidh alt nua."; diff --git a/OpenDocumentReader/it.lproj/Localizable.strings b/OpenDocumentReader/it.lproj/Localizable.strings index deab166..7a31ae4 100644 --- a/OpenDocumentReader/it.lproj/Localizable.strings +++ b/OpenDocumentReader/it.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Quella cella contiene una formula e resta com’è."; "edit_refused_formula_input" = "Digitare una formula non è ancora supportato."; +"edit_refused_array" = "Quella cella fa parte di una formula matrice e resta com’è."; "edit_refused_rich" = "Quella cella contiene più di testo semplice e resta com’è."; "edit_refused_generic" = "Questa modifica non è possibile qui."; "edit_refused_new_line" = "Un’interruzione di riga dentro un paragrafo non può essere salvata. Premi Invio per un nuovo paragrafo."; diff --git a/OpenDocumentReader/ja.lproj/Localizable.strings b/OpenDocumentReader/ja.lproj/Localizable.strings index 10f876d..8521b6f 100644 --- a/OpenDocumentReader/ja.lproj/Localizable.strings +++ b/OpenDocumentReader/ja.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "そのセルには数式が入っているため、そのままになります。"; "edit_refused_formula_input" = "数式の入力はまだ対応していません。"; +"edit_refused_array" = "このセルは配列数式の一部のため、変更できません。"; "edit_refused_rich" = "そのセルには書式付きの内容が入っているため、そのままになります。"; "edit_refused_generic" = "この編集はここではできません。"; "edit_refused_new_line" = "段落内の改行は保存できません。新しい段落にはReturnキーを押してください。"; diff --git a/OpenDocumentReader/pl.lproj/Localizable.strings b/OpenDocumentReader/pl.lproj/Localizable.strings index 1279c9f..fff43b8 100644 --- a/OpenDocumentReader/pl.lproj/Localizable.strings +++ b/OpenDocumentReader/pl.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Ta komórka zawiera formułę i pozostaje bez zmian."; "edit_refused_formula_input" = "Wpisywanie formuł nie jest jeszcze obsługiwane."; +"edit_refused_array" = "Ta komórka należy do formuły tablicowej i pozostaje bez zmian."; "edit_refused_rich" = "Ta komórka zawiera więcej niż zwykły tekst i pozostaje bez zmian."; "edit_refused_generic" = "Ta zmiana nie jest tu możliwa."; "edit_refused_new_line" = "Podziału wiersza wewnątrz akapitu nie można zapisać. Naciśnij Enter, aby zacząć nowy akapit."; diff --git a/OpenDocumentReader/pt-BR.lproj/Localizable.strings b/OpenDocumentReader/pt-BR.lproj/Localizable.strings index 7a19253..79e758f 100644 --- a/OpenDocumentReader/pt-BR.lproj/Localizable.strings +++ b/OpenDocumentReader/pt-BR.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Essa célula contém uma fórmula e fica como está."; "edit_refused_formula_input" = "Digitar uma fórmula ainda não é possível."; +"edit_refused_array" = "Essa célula faz parte de uma fórmula matricial e fica como está."; "edit_refused_rich" = "Essa célula contém mais do que texto simples e fica como está."; "edit_refused_generic" = "Essa edição não é possível aqui."; "edit_refused_new_line" = "Uma quebra de linha dentro de um parágrafo não pode ser salva. Pressione Enter para um novo parágrafo."; diff --git a/OpenDocumentReader/ru.lproj/Localizable.strings b/OpenDocumentReader/ru.lproj/Localizable.strings index 75cec93..56b1626 100644 --- a/OpenDocumentReader/ru.lproj/Localizable.strings +++ b/OpenDocumentReader/ru.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "В этой ячейке формула, она остаётся как есть."; "edit_refused_formula_input" = "Ввод формул пока не поддерживается."; +"edit_refused_array" = "Эта ячейка входит в формулу массива и остаётся без изменений."; "edit_refused_rich" = "В этой ячейке не только простой текст, она остаётся как есть."; "edit_refused_generic" = "Такое изменение здесь невозможно."; "edit_refused_new_line" = "Перенос строки внутри абзаца нельзя сохранить. Нажмите Ввод, чтобы начать новый абзац."; diff --git a/OpenDocumentReader/sl.lproj/Localizable.strings b/OpenDocumentReader/sl.lproj/Localizable.strings index 2348a4a..9bdb96c 100644 --- a/OpenDocumentReader/sl.lproj/Localizable.strings +++ b/OpenDocumentReader/sl.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Ta celica vsebuje formulo in ostane, kot je."; "edit_refused_formula_input" = "Vnos formul še ni podprt."; +"edit_refused_array" = "Ta celica je del matrične formule in ostane nespremenjena."; "edit_refused_rich" = "Ta celica vsebuje več kot navadno besedilo in ostane, kot je."; "edit_refused_generic" = "To urejanje tu ni mogoče."; "edit_refused_new_line" = "Preloma vrstice znotraj odstavka ni mogoče shraniti. Pritisnite Enter za nov odstavek."; diff --git a/OpenDocumentReader/tr.lproj/Localizable.strings b/OpenDocumentReader/tr.lproj/Localizable.strings index 78c9122..368f32b 100644 --- a/OpenDocumentReader/tr.lproj/Localizable.strings +++ b/OpenDocumentReader/tr.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "Bu hücrede bir formül var ve olduğu gibi kalır."; "edit_refused_formula_input" = "Formül yazma henüz desteklenmiyor."; +"edit_refused_array" = "Bu hücre bir dizi formülünün parçasıdır ve olduğu gibi kalır."; "edit_refused_rich" = "Bu hücrede düz metinden fazlası var ve olduğu gibi kalır."; "edit_refused_generic" = "Bu düzenleme burada yapılamaz."; "edit_refused_new_line" = "Paragraf içindeki satır sonu kaydedilemez. Yeni paragraf için Enter’a basın."; diff --git a/OpenDocumentReader/zh-Hans.lproj/Localizable.strings b/OpenDocumentReader/zh-Hans.lproj/Localizable.strings index 48ba883..269cfce 100644 --- a/OpenDocumentReader/zh-Hans.lproj/Localizable.strings +++ b/OpenDocumentReader/zh-Hans.lproj/Localizable.strings @@ -153,6 +153,7 @@ /* Why an edit was not taken */ "edit_refused_formula" = "该单元格包含公式,保持不变。"; "edit_refused_formula_input" = "暂不支持输入公式。"; +"edit_refused_array" = "该单元格属于数组公式,保持不变。"; "edit_refused_rich" = "该单元格包含的不只是纯文本,保持不变。"; "edit_refused_generic" = "此处无法进行该编辑。"; "edit_refused_new_line" = "段落内的换行无法保存。请按回车键开始新段落。"; diff --git a/OpenDocumentReaderTests/EditWorkflowTests.swift b/OpenDocumentReaderTests/EditWorkflowTests.swift index 5f772f4..97ceccc 100644 --- a/OpenDocumentReaderTests/EditWorkflowTests.swift +++ b/OpenDocumentReaderTests/EditWorkflowTests.swift @@ -70,6 +70,20 @@ class EditWorkflowTests: XCTestCase { XCTAssertEqual(evaluate("window.lastQuery") as? String, query) } + func testSearchShowsTheSelectedHitAndTheCount() throws { + openDocument() + controller.searchButton(controller.searchButton) + controller.searchBar.text = "simple" + + controller.searchBar(controller.searchBar, textDidChange: "simple") + waitUntil { self.controller.searchCountLabel.text?.hasPrefix("1/") == true } + let count = try XCTUnwrap(controller.searchCountLabel.text?.dropFirst(2)) + XCTAssertGreaterThan(Int(count) ?? 0, 1) + + controller.searchBarResultsListButtonClicked(controller.searchBar) + waitUntil { self.controller.searchCountLabel.text == "2/\(count)" } + } + func testCoveringTheReaderKeepsTheDocumentAndEdits() throws { openDocument() controller.editDocument() diff --git a/README.md b/README.md index 49519d4..ae40af4 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,16 @@ Security blocks plain HTTP. one and then moves it into place. - The page talks to the app through one `WKScriptMessageHandler`. +## Search + +The search bar calls `odr.search`, and the page sends the count back as +`searchChange`. Do not use the find of the web view. odrcore lays out only the +part near the viewport, and that find does not reach the rest. + +The count sits in a pill below the bar, because the search field does not show +a right view. Lazy loading stays off, because a print from the web view leaves +a part that has not loaded empty. + ## Formatting `scripts/format.sh` runs `swift-format` from the active Xcode toolchain, with