Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Core/Sources/Core/InputUtils/CandidatePresentationContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,19 @@ public struct CandidatePresentation: Sendable {
public init(candidate: Candidate, displayContext: CandidatePresentationContext = .init()) {
self.candidate = candidate
self.displayContext = displayContext
if self.displayContext.annotationText == nil {
self.displayContext.annotationText = Self.symbolAnnotation(for: candidate.text)
}
}

private static func symbolAnnotation(for text: String) -> String? {
switch text {
case "-": "半角ハイフン"
case "-": "全角ハイフン"
case "ー": "半角長音符"
case "ー": "全角長音符"
case "−": "マイナス記号"
default: nil
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Core
import Foundation
import KanaKanjiConverterModuleWithDefaultDictionary
import Testing

private func symbolCandidate(_ text: String) -> Candidate {
Candidate(text: text, value: 0, composingCount: .surfaceCount(text.count), lastMid: 0, data: [])
}

@Test(arguments: [
("-", "半角ハイフン"), ("-", "全角ハイフン"), ("ー", "半角長音符"),
("ー", "全角長音符"), ("−", "マイナス記号")
])
func testSymbolCandidateAnnotation(text: String, annotation: String) throws {
let presentation = CandidatePresentation(candidate: symbolCandidate(text))
#expect(presentation.candidate.text == text)
#expect(presentation.displayContext.annotationText == annotation)
let transported = try JSONDecoder().decode(
ConverterCandidatePresentation.self,
from: JSONEncoder().encode(ConverterCandidatePresentation(presentation))
)
#expect(transported.text == text)
#expect(transported.annotationText == annotation)
}

@Test(arguments: ["", "コーヒー", "ーー", "--", " -", "ー ", "A-B", "1−2", "漢字"])
func testSymbolAnnotationRequiresEntireCandidate(text: String) {
#expect(CandidatePresentation(candidate: symbolCandidate(text)).displayContext.annotationText == nil)
}

@Test func testSymbolAnnotationPreservesExistingContext() {
let presentation = CandidatePresentation(
candidate: symbolCandidate("ー"),
displayContext: .init(annotationText: "半角カナ", extraValues: ["source": "additional"])
)
#expect(presentation.displayContext.annotationText == "半角カナ")
#expect(presentation.displayContext.extraValues == ["source": "additional"])
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ Thanks to authors!!

## Acknowledgement
本プロジェクトは情報処理推進機構(IPA)による[2024年度未踏IT人材発掘・育成事業](https://www.ipa.go.jp/jinzai/mitou/it/2024/koubokekka.html)の支援を受けて開発を行いました。

### 記号候補の説明

単独の `-`・`-`・`ー`・`ー`・`−` の変換候補に、それぞれ「半角ハイフン」「全角ハイフン」「半角長音符」「全角長音符」「マイナス記号」を表示します。候補の文字や順序は変わりません。語中の記号には説明を付けず、既存の「半角カナ」などの説明がある場合はそちらを優先します。
16 changes: 10 additions & 6 deletions azooKeyMac/InputController/CandidateWindow/CandidateView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class CandidatesViewController: BaseCandidateViewController {
}

cell.candidateTextField.attributedStringValue = attributedString
cell.candidateTextField.setAccessibilityLabel(
annotationText.map { "\(displayText)、\($0)" } ?? displayText
)
cell.candidateAnnotationTextField.setAccessibilityElement(false)

if let annotationText {
let annotationString = NSAttributedString(
Expand Down Expand Up @@ -100,12 +104,12 @@ class CandidatesViewController: BaseCandidateViewController {
}

override func getWindowWidth(maxContentWidth: CGFloat) -> CGFloat {
let hasAnnotation = self.candidates.contains { $0.annotationText != nil }
if self.showCandidateIndex {
return maxContentWidth + 48 + (hasAnnotation ? 56 : 0)
} else {
return maxContentWidth + 20 + (hasAnnotation ? 56 : 0)
}
let annotationTexts = self.candidates.compactMap(\.annotationText)
let annotationWidth = annotationTexts.isEmpty ? 0 : self.getMaxTextWidth(
candidates: annotationTexts,
font: .systemFont(ofSize: 12)
) + 8
return maxContentWidth + (self.showCandidateIndex ? 48 : 20) + annotationWidth
}
}

Expand Down
24 changes: 24 additions & 0 deletions azooKeyMacTests/CandidateSelectionFeedbackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,28 @@ final class CandidateSelectionFeedbackTests: XCTestCase {
XCTAssertEqual(controller.currentSelectedRow, 1)
XCTAssertEqual(delegate.selectedRows, [1])
}

func testSymbolAnnotationIsAccessibleAndClearedWhenCellIsReused() {
let controller = CandidatesViewController()
controller.candidates = [
ConverterCandidatePresentation(text: "-", annotationText: "半角ハイフン"),
ConverterCandidatePresentation(text: "文章")
]
let cell = CandidateTableCellView()
controller.configureCellView(cell, forRow: 0)
XCTAssertFalse(cell.candidateAnnotationTextField.isHidden)
XCTAssertEqual(cell.candidateTextField.accessibilityLabel(), "-、半角ハイフン")
controller.configureCellView(cell, forRow: 1)
XCTAssertTrue(cell.candidateAnnotationTextField.isHidden)
XCTAssertEqual(cell.candidateTextField.accessibilityLabel(), "文章")
}

func testWindowWidthIncludesFullAnnotation() {
let controller = CandidatesViewController()
controller.candidates = [ConverterCandidatePresentation(text: "-", annotationText: "半角ハイフン")]
let width = controller.getWindowWidth(maxContentWidth: 18)
let annotationWidth = controller.getMaxTextWidth(candidates: ["半角ハイフン"], font: .systemFont(ofSize: 12))
XCTAssertGreaterThanOrEqual(width, 18 + 20 + 8 + annotationWidth)
}

}