@@ -168,3 +168,105 @@ struct MenuCardSectionContainerView<Content: View>: View {
168168 }
169169 }
170170}
171+
172+ @MainActor
173+ final class PersistentMenuActionItemView : NSView , MenuCardHighlighting {
174+ private let backgroundView = NSView ( )
175+ private let imageView = NSImageView ( )
176+ private let titleField : NSTextField
177+ private let shortcutField : NSTextField ?
178+ private let onClick : ( ) -> Void
179+
180+ init (
181+ title: String ,
182+ systemImageName: String ? ,
183+ shortcutText: String ? ,
184+ width: CGFloat ,
185+ onClick: @escaping ( ) -> Void )
186+ {
187+ self . titleField = NSTextField ( labelWithString: title)
188+ self . shortcutField = shortcutText. map ( NSTextField . init ( labelWithString: ) )
189+ self . onClick = onClick
190+ super. init ( frame: NSRect ( origin: . zero, size: NSSize ( width: width, height: 28 ) ) )
191+ self . setupView ( systemImageName: systemImageName)
192+ self . setHighlighted ( false )
193+ }
194+
195+ @available ( * , unavailable)
196+ required init ? ( coder: NSCoder ) {
197+ fatalError ( " init(coder:) has not been implemented " )
198+ }
199+
200+ override func acceptsFirstMouse( for event: NSEvent ? ) -> Bool {
201+ true
202+ }
203+
204+ override func mouseUp( with event: NSEvent ) {
205+ guard event. type == . leftMouseUp else { return }
206+ self . onClick ( )
207+ }
208+
209+ func setHighlighted( _ highlighted: Bool ) {
210+ let primaryColor = highlighted ? NSColor . selectedMenuItemTextColor : NSColor . controlTextColor
211+ let secondaryColor = highlighted ? NSColor . selectedMenuItemTextColor : NSColor . secondaryLabelColor
212+ self . backgroundView. isHidden = !highlighted
213+ self . titleField. textColor = primaryColor
214+ self . shortcutField? . textColor = secondaryColor
215+ self . imageView. contentTintColor = primaryColor
216+ }
217+
218+ private func setupView( systemImageName: String ? ) {
219+ self . backgroundView. wantsLayer = true
220+ self . backgroundView. layer? . cornerRadius = 6
221+ self . backgroundView. layer? . backgroundColor = NSColor . selectedContentBackgroundColor. cgColor
222+ self . backgroundView. translatesAutoresizingMaskIntoConstraints = false
223+ self . addSubview ( self . backgroundView)
224+
225+ if let systemImageName,
226+ let image = NSImage ( systemSymbolName: systemImageName, accessibilityDescription: nil )
227+ {
228+ image. isTemplate = true
229+ image. size = NSSize ( width: 16 , height: 16 )
230+ self . imageView. image = image
231+ }
232+ self . imageView. translatesAutoresizingMaskIntoConstraints = false
233+
234+ self . titleField. font = NSFont . menuFont ( ofSize: NSFont . systemFontSize)
235+ self . titleField. lineBreakMode = . byTruncatingTail
236+ self . titleField. setContentCompressionResistancePriority ( . defaultLow, for: . horizontal)
237+ self . titleField. translatesAutoresizingMaskIntoConstraints = false
238+
239+ let spacer = NSView ( )
240+ spacer. translatesAutoresizingMaskIntoConstraints = false
241+ spacer. setContentHuggingPriority ( . defaultLow, for: . horizontal)
242+
243+ let stack = NSStackView ( )
244+ stack. orientation = . horizontal
245+ stack. alignment = . centerY
246+ stack. spacing = 8
247+ stack. translatesAutoresizingMaskIntoConstraints = false
248+ stack. addArrangedSubview ( self . imageView)
249+ stack. addArrangedSubview ( self . titleField)
250+ stack. addArrangedSubview ( spacer)
251+ if let shortcutField {
252+ shortcutField. font = NSFont . menuFont ( ofSize: NSFont . smallSystemFontSize)
253+ shortcutField. translatesAutoresizingMaskIntoConstraints = false
254+ stack. addArrangedSubview ( shortcutField)
255+ }
256+ self . addSubview ( stack)
257+
258+ NSLayoutConstraint . activate ( [
259+ self . backgroundView. leadingAnchor. constraint ( equalTo: self . leadingAnchor, constant: 6 ) ,
260+ self . backgroundView. trailingAnchor. constraint ( equalTo: self . trailingAnchor, constant: - 6 ) ,
261+ self . backgroundView. topAnchor. constraint ( equalTo: self . topAnchor, constant: 2 ) ,
262+ self . backgroundView. bottomAnchor. constraint ( equalTo: self . bottomAnchor, constant: - 2 ) ,
263+
264+ self . imageView. widthAnchor. constraint ( equalToConstant: 18 ) ,
265+ self . imageView. heightAnchor. constraint ( equalToConstant: 18 ) ,
266+
267+ stack. leadingAnchor. constraint ( equalTo: self . leadingAnchor, constant: 12 ) ,
268+ stack. trailingAnchor. constraint ( equalTo: self . trailingAnchor, constant: - 12 ) ,
269+ stack. centerYAnchor. constraint ( equalTo: self . centerYAnchor) ,
270+ ] )
271+ }
272+ }
0 commit comments