.../Source/App/SearchAndPreview/SearchAndPreviewView.swift
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | //  | 
2  |  | //  SearchView.swift  | 
3  |  | //  CutBox  | 
4  |  | //  | 
5  |  | //  Created by Jason Milkins on 24/3/18.  | 
6  |  | //  Copyright © 2018-2023 ocodo. All rights reserved.  | 
7  |  | //  | 
8  |  |  | 
9  |  | import Foundation  | 
10  |  | import Cocoa  | 
11  |  | import RxSwift  | 
12  |  | import RxCocoa  | 
13  |  | import Carbon  | 
14  |  |  | 
15  |  | /// Main view of CutBox, displayed via `PopupController`  | 
16  |  | /// communicates with the `CutBoxController` via events<`SearchViewEvents`>  | 
17  |  | /// Extends `SearchAndPreviewViewBase`  | 
18  |  | class SearchAndPreviewView: SearchAndPreviewViewBase { | 
19  |  |  | 
20  |  |     @IBOutlet weak var searchModeToggle: CutBoxBaseButton!  | 
21  |  |     @IBOutlet weak var jsIconButton: CutBoxBaseButton!  | 
22  |  |     @IBOutlet weak var timeFilterText: ValidIndicatorTextField!  | 
23  |  |     @IBOutlet weak var timeFilterLabel: CutBoxBaseTextField!  | 
24  |  |     @IBOutlet weak var historyScopeImageButton: CutBoxBaseButton!  | 
25  |  |  | 
26  | 79  |     var events = PublishSubject<SearchViewEvents>()  | 
27  |  |  | 
28  | 48  |     override func awakeFromNib() { | 
29  | 48  |         self.setAccessibilityIdentifier("searchAndPreviewView") | 
30  | 48  |         setupSearchText()  | 
31  | 48  |         setupSearchModeToggle()  | 
32  | 48  |         setupSearchScopeToggle()  | 
33  | 48  |         setupHistoryScopeButton()  | 
34  | 48  |         setupTimeFilter()  | 
35  | 48  |         setupJSIconButton()  | 
36  | 48  |         super.awakeFromNib()  | 
37  | 48  |         applyTheme()  | 
38  | 48  |     }  | 
39  |  |  | 
40  | 48  |     func setupJSIconButton() { | 
41  | 48  |         jsIconButton.setAccessibilityIdentifier("jsIconButton") | 
42  | 48  |         jsIconButton.rx.tap  | 
43  | 48  |             .map { .selectJavascriptFunction } | 
44  | 48  |             .bind(to: self.events)  | 
45  | 48  |             .disposed(by: disposeBag)  | 
46  | 48  |     }  | 
47  |  |  | 
48  | 102  |     func setSearchModeButton(mode: HistorySearchMode) { | 
49  | 102  |         let color = [NSAttributedString.Key.foregroundColor: prefs.currentTheme.clip.textColor]  | 
50  | 102  |         let titleString = NSAttributedString(string: mode.name, attributes: color)  | 
51  | 102  |  | 
52  | 102  |         self.searchModeToggle?.attributedTitle = titleString  | 
53  | 102  |         self.searchModeToggle?.toolTip = mode.toolTip  | 
54  | 102  |     }  | 
55  |  |  | 
56  | 48  |     private func setupHistoryScopeButton() { | 
57  | 48  |         colorizeHistoryScopeIcon(alpha: 0.4,  | 
58  | 48  |                                  color: prefs.currentTheme.searchText.placeholderTextColor)  | 
59  | 48  |  | 
60  | 48  |         self.historyScopeImageButton.rx.tap  | 
61  | 48  |             .bind(onNext: historyScopeClicked) $s15CutBoxUnitTests20SearchAndPreviewViewC23setupHistoryScopeButton33_B55705268F368941FE232CCBCA4658DDLLyyFyycACcfu_ Line  | Count  | Source  |  61  | 48  |             .bind(onNext: historyScopeClicked)  |  
 Unexecuted instantiation: $s15CutBoxUnitTests20SearchAndPreviewViewC23setupHistoryScopeButton33_B55705268F368941FE232CCBCA4658DDLLyyFyycACcfu_yycfu0_  | 
62  | 48  |             .disposed(by: disposeBag)  | 
63  | 48  |     }  | 
64  |  |  | 
65  |  |     private func colorizeHistoryScopeIcon(image: NSImage = CutBoxImageRef.historyClockFaceWhite.image(true),  | 
66  |  |                                           tooltip: String = "search_time_filter_label_hint".l7n,  | 
67  |  |                                           alpha: Double = 0.75,  | 
68  | 98  |                                           color: NSColor = NSColor.white) { | 
69  | 98  |         let image = image  | 
70  | 98  |         let blended = image.tint(color: color)  | 
71  | 98  |  | 
72  | 98  |         self.historyScopeImageButton?.alphaValue = alpha  | 
73  | 98  |         self.historyScopeImageButton?.image = blended  | 
74  | 98  |         self.historyScopeImageButton?.toolTip = tooltip  | 
75  | 98  |     }  | 
76  |  |  | 
77  | 0  |     private func historyScopeClicked() { | 
78  | 0  |         self.events.onNext(.toggleTimeFilter)  | 
79  | 0  |     }  | 
80  |  |  | 
81  | 48  |     private func setupSearchModeToggle() { | 
82  | 48  |         let mode = HistoryService.shared.searchMode  | 
83  | 48  |         setSearchModeButton(mode: mode)  | 
84  | 48  |  | 
85  | 48  |         self.searchModeToggle.rx.tap  | 
86  | 48  |             .map { .toggleSearchMode } | 
87  | 48  |             .bind(to: self.events)  | 
88  | 48  |             .disposed(by: disposeBag)  | 
89  | 48  |     }  | 
90  |  |  | 
91  | 99  |     func setSearchScopeButton(favoritesOnly: Bool) { | 
92  | 99  |         if favoritesOnly { | 
93  | 1  |             colorizeMagnifier(  | 
94  | 1  |                 image: CutBoxImageRef.star.image(),  | 
95  | 1  |                 tooltip: "search_scope_tooltip_favorites".l7n,  | 
96  | 1  |                 color: prefs.currentTheme.searchText.placeholderTextColor  | 
97  | 1  |             )  | 
98  | 99  |         } else { | 
99  | 98  |             colorizeMagnifier(color: prefs.currentTheme.searchText.placeholderTextColor)  | 
100  | 99  |         }  | 
101  | 99  |     }  | 
102  |  |  | 
103  | 4  |     override func setTextScale() { | 
104  | 4  |         super.setTextScale()  | 
105  | 4  |     }  | 
106  |  |  | 
107  | 48  |     private func setupSearchScopeToggle() { | 
108  | 48  |         let favoritesOnly = HistoryService.shared.favoritesOnly  | 
109  | 48  |         setSearchScopeButton(favoritesOnly: favoritesOnly)  | 
110  | 48  |  | 
111  | 48  |         self.searchScopeImageButton.rx.tap  | 
112  | 48  |             .map { .toggleSearchScope } | 
113  | 48  |             .bind(to: self.events)  | 
114  | 48  |             .disposed(by: disposeBag)  | 
115  | 48  |     }  | 
116  |  |  | 
117  | 48  |     private func setupSearchText() { | 
118  | 48  |         self.searchText.delegate = self  | 
119  | 48  |         self.searchText.isFieldEditor = true  | 
120  | 48  |     }  | 
121  |  |  | 
122  | 48  |     private func setupTimeFilter() { | 
123  | 48  |          self.timeFilterText.rx.text  | 
124  | 48  |             .compactMap { $0 } | 
125  | 48  |             .subscribe { self.onTimeFilterTextChanged(text: $0) } | 
126  | 48  |             .disposed(by: disposeBag)  | 
127  | 48  |  | 
128  | 48  |         self.timeFilterText.keyUp  | 
129  | 48  |             .subscribe(onNext: { (event: NSEvent) in | 
130  | 0  |                 if event.keyCode == 36 { | 
131  | 0  |                     self.window?.makeFirstResponder(self.searchText)  | 
132  | 0  |                 }  | 
133  | 0  |             })  | 
134  | 48  |             .disposed(by: disposeBag)  | 
135  | 48  |  | 
136  | 48  |         self.timeFilterLabel.isHidden = true  | 
137  | 48  |         self.timeFilterText.isHidden = true  | 
138  | 48  |         self.timeFilterText.placeholderString = "...".l7n  | 
139  | 48  |         self.timeFilterText.isValid = false  | 
140  | 48  |     }  | 
141  |  |  | 
142  | 1  |     func toggleTimeFilter() { | 
143  | 1  |         self.timeFilterLabel.isHidden.toggle()  | 
144  | 1  |         self.timeFilterText.isHidden.toggle()  | 
145  | 1  |  | 
146  | 1  |         if self.timeFilterText.isHidden { | 
147  | 1  |             self.window?.makeFirstResponder(self.searchText)  | 
148  | 1  |             self.prefs.savedTimeFilterValue = self.timeFilterText.stringValue  | 
149  | 1  |             self.timeFilterText.stringValue = ""  | 
150  | 1  |         } else { | 
151  | 0  |             let newText: String = self.prefs.savedTimeFilterValue  | 
152  | 0  |             self.window?.makeFirstResponder(self.timeFilterText)  | 
153  | 0  |             self.timeFilterText.stringValue = newText  | 
154  | 0  |             DispatchQueue.main.async { self.onTimeFilterTextChanged(text: newText) } | 
155  | 1  |         }  | 
156  | 1  |     }  | 
157  |  |  | 
158  | 48  |     func onTimeFilterTextChanged(text: String) { | 
159  | 48  |         let filter = TimeFilterValidator(value: text)  | 
160  | 48  |         self.timeFilterText?.isValid = filter.isValid  | 
161  | 48  |  | 
162  | 48  |         if let seconds = filter.seconds { | 
163  | 0  |             let formatted = TimeFilterValidator.secondsToTime(seconds: Int(seconds))  | 
164  | 0  |             self.timeFilterLabel?.stringValue = String(format: "search_time_filter_label_active".l7n, formatted)  | 
165  | 0  |             self.events.onNext(.setTimeFilter(seconds: seconds))  | 
166  | 48  |         } else { | 
167  | 48  |             self.timeFilterLabel?.stringValue = "search_time_filter_label_hint".l7n  | 
168  | 48  |             self.events.onNext(.setTimeFilter(seconds: nil))  | 
169  | 48  |         }  | 
170  | 48  |     }  | 
171  |  |  | 
172  | 0  |     @objc func removeSelectedItems() { | 
173  | 0  |         self.events.onNext(.removeSelected)  | 
174  | 0  |     }  | 
175  |  |  | 
176  | 0  |     @objc func toggleFavoriteItems() { | 
177  | 0  |         self.events.onNext(.toggleFavorite)  | 
178  | 0  |     }  | 
179  |  |  | 
180  | 75  |     func setupClipItemsContextMenu() { | 
181  | 75  |         let removeItem = CutBoxBaseMenuItem(title: "context_menu_remove_selected".l7n,  | 
182  | 75  |                                 action: #selector(removeSelectedItems),  | 
183  | 75  |                                 keyEquivalent: "")  | 
184  | 75  |  | 
185  | 75  |         let favoriteItem = CutBoxBaseMenuItem(title: "context_menu_favorite".l7n,  | 
186  | 75  |                                   action: #selector(toggleFavoriteItems),  | 
187  | 75  |                                   keyEquivalent: "")  | 
188  | 75  |  | 
189  | 75  |         let contextMenu = CutBoxBaseMenu()  | 
190  | 75  |         contextMenu.addItem(removeItem)  | 
191  | 75  |         contextMenu.addItem(favoriteItem)  | 
192  | 75  |  | 
193  | 75  |         self.itemsList?.menu = contextMenu  | 
194  | 75  |     }  | 
195  |  |  | 
196  | 50  |     override func applyTheme() { | 
197  | 50  |         super.applyTheme()  | 
198  | 50  |  | 
199  | 50  |         let theme = prefs.currentTheme  | 
200  | 50  |  | 
201  | 50  |         timeFilterLabel?.textColor = theme.searchText.placeholderTextColor  | 
202  | 50  |  | 
203  | 50  |         colorizeHistoryScopeIcon(alpha: 0.6,  | 
204  | 50  |                                  color: theme.searchText.placeholderTextColor)  | 
205  | 50  |         setSearchModeButton(mode: HistoryService.shared.searchMode)  | 
206  | 50  |         setSearchScopeButton(favoritesOnly: HistoryService.shared.favoritesOnly)  | 
207  | 50  |     }  | 
208  |  | }  |