.../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 | | } |