diff --git a/Source/InputMethodController.swift b/Source/InputMethodController.swift index eddb65d6..b3033aa7 100644 --- a/Source/InputMethodController.swift +++ b/Source/InputMethodController.swift @@ -575,6 +575,17 @@ extension McBopomofoInputMethodController: CandidateControllerDelegate { func candidateController(_ controller: CandidateController, didSelectCandidateAtIndex index: UInt) { let client = currentCandidateClient + if let state = state as? InputState.SymbolTable, + let node = state.node.children?[Int(index)] { + if let children = node.children, !children.isEmpty { + self.handle(state: .SymbolTable(node: node, useVerticalMode: state.useVerticalMode), client: currentCandidateClient) + } else { + self.handle(state: .Committing(poppedText: node.title), client: client) + self.handle(state: .Empty(), client: client) + } + return + } + if let state = state as? InputState.ChoosingCandidate { let selectedValue = state.candidates[Int(index)] keyHandler.fixNode(value: selectedValue) @@ -596,7 +607,10 @@ extension McBopomofoInputMethodController: CandidateControllerDelegate { } else { handle(state: inputting, client: client) } - } else if let state = state as? InputState.AssociatedPhrases { + return + } + + if let state = state as? InputState.AssociatedPhrases { let selectedValue = state.candidates[Int(index)] handle(state: .Committing(poppedText: selectedValue), client: currentCandidateClient) if Preferences.associatedPhrasesEnabled, diff --git a/Source/InputState.swift b/Source/InputState.swift index 9f9382c7..a512a7b9 100644 --- a/Source/InputState.swift +++ b/Source/InputState.swift @@ -317,4 +317,53 @@ class InputState: NSObject { "" } } + + @objc (InputStateSymbolTable) + class SymbolTable: ChoosingCandidate { + @objc var node: SymbolNode + + @objc init(node: SymbolNode, useVerticalMode: Bool) { + self.node = node + let candidates = node.children?.map { $0.title } ?? [String]() + super.init(composingBuffer: "", cursorIndex: 0, candidates: candidates, useVerticalMode: useVerticalMode) + } + + override var description: String { + "" + } + } + +} + +@objc class SymbolNode: NSObject { + @objc var title: String + @objc var children: [SymbolNode]? + + @objc init(_ title: String, _ children: [SymbolNode]? = nil) { + self.title = title + self.children = children + super.init() + } + + @objc init(_ title: String, symbols: String) { + self.title = title + self.children = Array(symbols).map { SymbolNode(String($0), nil) } + super.init() + } + + @objc static let root: SymbolNode = SymbolNode("/", [ + SymbolNode("…"), + SymbolNode("※"), + SymbolNode("常用符號", symbols:",、。.?!;:‧‥﹐﹒˙·‘’“”〝〞‵′〃~$%@&#*"), + SymbolNode("左右括號", symbols:"()「」〔〕{}〈〉『』《》【】﹙﹚﹝﹞﹛﹜"), + SymbolNode("上下括號", symbols:"︵︶﹁﹂︹︺︷︸︿﹀﹃﹄︽︾︻︼"), + SymbolNode("希臘字母", symbols:"αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"), + SymbolNode("數學符號", symbols:"+-×÷=≠≒∞±√<>﹤﹥≦≧∩∪ˇ⊥∠∟⊿㏒㏑∫∮∵∴╳﹢"), + SymbolNode("特殊圖形", symbols:"↑↓←→↖↗↙↘㊣◎○●⊕⊙○●△▲☆★◇◆□■▽▼§¥〒¢£※♀♂"), + SymbolNode("Unicode", symbols:"♨☀☁☂☃♠♥♣♦♩♪♫♬☺☻"), + SymbolNode("單線框", symbols:"├─┼┴┬┤┌┐╞═╪╡│▕└┘╭╮╰╯"), + SymbolNode("雙線框", symbols:"╔╦╗╠═╬╣╓╥╖╒╤╕║╚╩╝╟╫╢╙╨╜╞╪╡╘╧╛"), + SymbolNode("填色方塊", symbols:"_ˍ▁▂▃▄▅▆▇█▏▎▍▌▋▊▉◢◣◥◤"), + SymbolNode("線段", symbols:"﹣﹦≡|∣∥–︱—︳╴¯ ̄﹉﹊﹍﹎﹋﹌﹏︴∕﹨╱╲/\"), + ]) } diff --git a/Source/KeyHandler.mm b/Source/KeyHandler.mm index a4b2d4f7..feffa4b6 100644 --- a/Source/KeyHandler.mm +++ b/Source/KeyHandler.mm @@ -489,20 +489,13 @@ static NSString *const kGraphVizOutputfile = @"/tmp/McBopomofo-visualization.dot // MARK: Punctuation list if ((char)charCode == '`') { - if (_languageModel->hasUnigramsForKey("_punctuation_list")) { - if (_bpmfReadingBuffer->isEmpty()) { - _builder->insertReadingAtCursor("_punctuation_list"); - NSString *poppedText = [self _popOverflowComposingTextAndWalk]; - InputStateInputting *inputting = (InputStateInputting *)[self buildInputtingState]; - inputting.poppedText = poppedText; - stateCallback(inputting); - InputStateChoosingCandidate *choosingCandidate = [self _buildCandidateState:inputting useVerticalMode:input.useVerticalMode]; - stateCallback(choosingCandidate); - } else { // If there is still unfinished bpmf reading, ignore the punctuation - errorCallback(); - } - return YES; - } + InputStateEmpty *empty = [[InputStateEmpty alloc] init]; + stateCallback(empty); + + SymbolNode *root = [SymbolNode root]; + InputStateSymbolTable *symbolState = [[InputStateSymbolTable alloc] initWithNode:root useVerticalMode:input.useVerticalMode]; + stateCallback(symbolState); + return YES; } // MARK: Punctuation