KMNBotaSearchTopView.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. //
  2. // KMNBotaSearchTopVIew.swift
  3. // PDF Reader Pro
  4. //
  5. // Created by User-Tangchao on 2024/12/1.
  6. //
  7. import Cocoa
  8. import KMComponentLibrary
  9. enum KMNBotaSearchTopItemKey: Int {
  10. case search = 1
  11. case replace = 2
  12. case `switch` = 3
  13. case previous = 4
  14. case next = 5
  15. case replaceText
  16. case replaceAllText
  17. }
  18. typealias KMNBotaSearchTopViewEnterAction = (_ string: String) -> Void
  19. class KMNBotaSearchTopView: NSView, NibLoadable {
  20. @IBOutlet weak var topBox: NSBox!
  21. @IBOutlet weak var centerBox: NSBox!
  22. @IBOutlet weak var bottomBox: NSBox!
  23. @IBOutlet weak var centerHeightConst: NSLayoutConstraint!
  24. var itemClick: KMCommonClickBlock?
  25. var valueDidChange: KMValueDidChangeBlock?
  26. var replaceValueDidChange: KMValueDidChangeBlock?
  27. var enterAction: KMNBotaSearchTopViewEnterAction?
  28. private var topHeight_: CGFloat = 40
  29. private var centerHeight_ : CGFloat = 72
  30. private var bottomHeight_: CGFloat = 32
  31. private var vSpace_: CGFloat = 8
  32. private lazy var searchButton_: ComponentButton = {
  33. let view = ComponentButton()
  34. view.properties = ComponentButtonProperty(type: .text_gray, size: .xxxs, state: .normal, onlyIcon: true, icon: NSImage(named: "KMImageNameBotaSearch"))
  35. view.setTarget(self, action: #selector(searchAction))
  36. return view
  37. }()
  38. private lazy var searchInput_: NSTextField = {
  39. let view = NSTextField()
  40. view.placeholderString = KMLocalizedString("Search") + " (⌥⌘F)"
  41. view.isBordered = false
  42. view.drawsBackground = false
  43. view.delegate = self
  44. return view
  45. }()
  46. private lazy var replaceButton_: ComponentButton = {
  47. let view = ComponentButton()
  48. view.properties = ComponentButtonProperty(type: .text_gray_low, size: .xxs, state: .normal, onlyIcon: true, icon: NSImage(named: "KMImageNameBotaSearchReplace"), keepPressState: false)
  49. view.setTarget(self, action: #selector(replaceAction))
  50. return view
  51. }()
  52. private lazy var switchButton_: ComponentButton = {
  53. let view = ComponentButton()
  54. view.properties = ComponentButtonProperty(type: .text_gray_low, size: .xxs, state: .normal, onlyIcon: true, icon: NSImage(named: "KMImageNameBotaSearchSwitch"), keepPressState: false)
  55. view.setTarget(self, action: #selector(switchAction))
  56. return view
  57. }()
  58. private lazy var centerView_: KMNSearchReplaceItemView = {
  59. let view = KMNSearchReplaceItemView()
  60. return view
  61. }()
  62. private lazy var resultLabel_: NSTextField = {
  63. let view = NSTextField(labelWithString: "")
  64. view.font = ComponentLibrary.shared.getFontFromKey("mac/body-xs-medium")
  65. return view
  66. }()
  67. private lazy var previousButton_: ComponentButton = {
  68. let view = ComponentButton()
  69. view.properties = ComponentButtonProperty(type: .text_gray_low, size: .xxxs, state: .normal, onlyIcon: true, icon: NSImage(named: "KMImageNameBotaSearchPreviousDisable"), keepPressState: false)
  70. view.setTarget(self, action: #selector(previousAction))
  71. return view
  72. }()
  73. private lazy var nextButton_: ComponentButton = {
  74. let view = ComponentButton()
  75. view.properties = ComponentButtonProperty(type: .text_gray_low, size: .xxxs, state: .normal, onlyIcon: true, icon: NSImage(named: "KMImageNameBotaSearchNext"), keepPressState: false)
  76. view.setTarget(self, action: #selector(nextActon))
  77. return view
  78. }()
  79. private lazy var bottomLine_: NSView = {
  80. let view = NSView()
  81. view.wantsLayer = true
  82. return view
  83. }()
  84. var searchInput: NSTextField {
  85. get {
  86. return searchInput_
  87. }
  88. }
  89. var replaceInput: ComponentInput {
  90. get {
  91. return centerView_.input
  92. }
  93. }
  94. var replaceButton: ComponentButton {
  95. get {
  96. return centerView_.replaceButton
  97. }
  98. }
  99. var replaceAllButton: ComponentButton {
  100. get {
  101. return centerView_.replaceAllButton
  102. }
  103. }
  104. var previousButton: ComponentButton {
  105. get {
  106. return previousButton_
  107. }
  108. }
  109. var nextButton: ComponentButton {
  110. get {
  111. return nextButton_
  112. }
  113. }
  114. var resultLabel: NSTextField {
  115. get {
  116. return resultLabel_
  117. }
  118. }
  119. var bottomLine: NSView {
  120. get {
  121. return bottomLine_
  122. }
  123. }
  124. var inputValue: String? {
  125. get {
  126. return searchInput_.stringValue
  127. }
  128. set {
  129. searchInput_.stringValue = newValue ?? ""
  130. }
  131. }
  132. var replaceText: String? {
  133. get {
  134. return centerView_.inputValue
  135. }
  136. set {
  137. centerView_.inputValue = newValue ?? ""
  138. }
  139. }
  140. override func draw(_ dirtyRect: NSRect) {
  141. super.draw(dirtyRect)
  142. // Drawing code here.
  143. }
  144. override func awakeFromNib() {
  145. super.awakeFromNib()
  146. initSubviews()
  147. }
  148. func initSubviews() {
  149. topBox.borderWidth = 0
  150. topBox.contentView?.addSubview(searchButton_)
  151. searchButton_.km_add_leading_constraint(constant: 12)
  152. searchButton_.km_add_size_constraint(size: NSSize(width: 16, height: 16))
  153. searchButton_.km_add_centerY_constraint()
  154. topBox.contentView?.addSubview(searchInput_)
  155. searchInput_.km_add_leading_constraint(equalTo: searchButton_, attribute: .trailing, constant: 8)
  156. searchInput_.km_add_centerY_constraint()
  157. searchInput_.km_add_height_constraint(constant: 20)
  158. searchInput_.km_add_trailing_constraint(constant: -76)
  159. topBox.contentView?.addSubview(switchButton_)
  160. switchButton_.km_add_trailing_constraint(constant: -12)
  161. switchButton_.km_add_centerY_constraint()
  162. switchButton_.km_add_size_constraint(size: NSSize(width: 24, height: 24))
  163. topBox.contentView?.addSubview(replaceButton_)
  164. replaceButton_.km_add_trailing_constraint(equalTo: switchButton_, attribute: .leading, constant: -8)
  165. replaceButton_.km_add_centerY_constraint()
  166. replaceButton_.km_add_size_constraint(size: NSSize(width: 24, height: 24))
  167. topBox.contentView?.addSubview(bottomLine_)
  168. bottomLine.km_add_leading_constraint()
  169. bottomLine.km_add_trailing_constraint()
  170. bottomLine.km_add_height_constraint(constant: 2)
  171. bottomLine.km_add_bottom_constraint()
  172. centerBox.isHidden = true
  173. centerBox.borderWidth = 0
  174. centerBox.contentView = centerView_
  175. centerView_.itemClick = { [unowned self] idx, _ in
  176. if idx == 1 {
  177. replaceAllButton.properties.state = .normal
  178. replaceAllButton.reloadData()
  179. itemClick?(KMNBotaSearchTopItemKey.replaceAllText.rawValue)
  180. } else if idx == 2 {
  181. replaceButton.properties.state = .normal
  182. replaceButton.reloadData()
  183. itemClick?(KMNBotaSearchTopItemKey.replaceText.rawValue)
  184. }
  185. }
  186. centerView_.valueDidChange = { [unowned self] value, _ in
  187. if let data = value as? String {
  188. replaceValueDidChange?(replaceInput, [.newKey : replaceInput.properties.text])
  189. }
  190. }
  191. bottomBox.isHidden = true
  192. bottomBox.borderWidth = 0
  193. bottomBox.contentView?.addSubview(resultLabel_)
  194. resultLabel_.km_add_top_constraint(constant: 16)
  195. resultLabel_.km_add_leading_constraint(constant: 16)
  196. bottomBox.contentView?.addSubview(nextButton_)
  197. nextButton_.km_add_trailing_constraint(constant: -16)
  198. nextButton_.km_add_top_constraint(constant: 16)
  199. nextButton_.km_add_size_constraint(size: .init(width: 16, height: 16))
  200. bottomBox.contentView?.addSubview(previousButton_)
  201. previousButton_.km_add_trailing_constraint(equalTo: nextButton_, attribute: .leading, constant: -8)
  202. previousButton_.km_add_top_constraint(constant: 16)
  203. previousButton_.km_add_size_constraint(size: .init(width: 16, height: 16))
  204. }
  205. func showSearch() {
  206. centerBox.isHidden = true
  207. centerHeightConst.constant = 0
  208. }
  209. func showReplace() {
  210. centerBox.isHidden = false
  211. centerHeightConst.constant = centerHeight_
  212. }
  213. func showResult(type: KMNBotaSearchType) {
  214. bottomBox.isHidden = false
  215. }
  216. func hideResult(type: KMNBotaSearchType) {
  217. bottomBox.isHidden = true
  218. }
  219. func fetchContentHeight(type: KMNBotaSearchType, hasResult: Bool) -> CGFloat {
  220. var height: CGFloat = topHeight_
  221. if type == .search {
  222. } else if type == .replace {
  223. height += centerHeight_
  224. }
  225. height += (hasResult ? bottomHeight_+vSpace_ : 0)
  226. return height
  227. }
  228. // MARK: - Actions
  229. @objc func searchAction() {
  230. itemClick?(KMNBotaSearchTopItemKey.search.rawValue, searchButton_)
  231. }
  232. @objc func replaceAction() {
  233. itemClick?(KMNBotaSearchTopItemKey.replace.rawValue)
  234. }
  235. @objc func switchAction() {
  236. itemClick?(KMNBotaSearchTopItemKey.switch.rawValue)
  237. }
  238. @objc func previousAction() {
  239. itemClick?(KMNBotaSearchTopItemKey.previous.rawValue)
  240. }
  241. @objc func nextActon() {
  242. itemClick?(KMNBotaSearchTopItemKey.next.rawValue)
  243. }
  244. }
  245. extension KMNBotaSearchTopView: NSTextFieldDelegate {
  246. func controlTextDidChange(_ obj: Notification) {
  247. if searchInput_.isEqual(to: obj.object) {
  248. valueDidChange?(searchInput_, [.newKey : searchInput_.stringValue])
  249. }
  250. }
  251. func control(_ control: NSControl, textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool {
  252. if control == searchInput_, commandSelector == #selector(NSResponder.insertNewline(_:)) {
  253. print("Enter 键被按下 (来自 searchInput_)")
  254. print(searchInput_.stringValue)
  255. // 在这里执行你需要的操作,例如通知外部
  256. enterAction?(searchInput_.stringValue)
  257. return true // 阻止默认行为
  258. }
  259. return false
  260. }
  261. }