Skip to content

Commit a55bd4b

Browse files
committed
Added replace and match case toggle UI.
1 parent 3dccebd commit a55bd4b

File tree

6 files changed

+345
-54
lines changed

6 files changed

+345
-54
lines changed

Example/CodeEditSourceEditorExample/CodeEditSourceEditorExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ let package = Package(
2424
url: "https://github.com/CodeEditApp/CodeEditLanguages.git",
2525
exact: "0.1.20"
2626
),
27+
// CodeEditSymbols
28+
.package(
29+
url: "https://github.com/CodeEditApp/CodeEditSymbols.git",
30+
exact: "0.2.3"
31+
),
2732
// SwiftLint
2833
.package(
2934
url: "https://github.com/lukepistrol/SwiftLintPlugin",
@@ -43,7 +48,8 @@ let package = Package(
4348
dependencies: [
4449
"CodeEditTextView",
4550
"CodeEditLanguages",
46-
"TextFormation"
51+
"TextFormation",
52+
"CodeEditSymbols"
4753
],
4854
plugins: [
4955
.plugin(name: "SwiftLint", package: "SwiftLintPlugin")
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//
2+
// FindModePicker.swift
3+
// CodeEditSourceEditor
4+
//
5+
// Created by Austin Condiff on 4/10/25.
6+
//
7+
8+
import SwiftUI
9+
10+
struct FindModePicker: NSViewRepresentable {
11+
@Binding var mode: FindPanelMode
12+
@Binding var wrapAround: Bool
13+
@Environment(\.controlActiveState) var activeState
14+
let onToggleWrapAround: () -> Void
15+
16+
func makeNSView(context: Context) -> NSView {
17+
let container = NSView()
18+
container.wantsLayer = true
19+
20+
// Create the magnifying glass button
21+
let button = NSButton(frame: .zero)
22+
button.bezelStyle = .regularSquare
23+
button.isBordered = false
24+
button.controlSize = .small
25+
button.image = NSImage(systemSymbolName: "magnifyingglass", accessibilityDescription: nil)?
26+
.withSymbolConfiguration(.init(pointSize: 12, weight: .regular))
27+
button.imagePosition = .imageOnly
28+
button.target = context.coordinator
29+
button.action = #selector(Coordinator.openMenu(_:))
30+
31+
// Create the popup button
32+
let popup = NSPopUpButton(frame: .zero, pullsDown: false)
33+
popup.bezelStyle = .regularSquare
34+
popup.isBordered = false
35+
popup.controlSize = .small
36+
popup.font = .systemFont(ofSize: NSFont.systemFontSize(for: .small))
37+
popup.autoenablesItems = false
38+
39+
// Calculate the required width
40+
let font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .small))
41+
let maxWidth = FindPanelMode.allCases.map { mode in
42+
mode.displayName.size(withAttributes: [.font: font]).width
43+
}.max() ?? 0
44+
let totalWidth = maxWidth + 28 // Add padding for the chevron and spacing
45+
46+
// Create menu
47+
let menu = NSMenu()
48+
49+
// Add mode items
50+
FindPanelMode.allCases.forEach { mode in
51+
let item = NSMenuItem(title: mode.displayName, action: #selector(Coordinator.modeSelected(_:)), keyEquivalent: "")
52+
item.target = context.coordinator
53+
item.tag = mode == .find ? 0 : 1
54+
menu.addItem(item)
55+
}
56+
57+
// Add separator
58+
menu.addItem(.separator())
59+
60+
// Add wrap around item
61+
let wrapItem = NSMenuItem(title: "Wrap Around", action: #selector(Coordinator.toggleWrapAround(_:)), keyEquivalent: "")
62+
wrapItem.target = context.coordinator
63+
wrapItem.state = wrapAround ? .on : .off
64+
menu.addItem(wrapItem)
65+
66+
popup.menu = menu
67+
popup.selectItem(at: mode == .find ? 0 : 1)
68+
69+
// Add subviews
70+
container.addSubview(button)
71+
container.addSubview(popup)
72+
73+
// Set up constraints
74+
button.translatesAutoresizingMaskIntoConstraints = false
75+
popup.translatesAutoresizingMaskIntoConstraints = false
76+
77+
NSLayoutConstraint.activate([
78+
button.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 4),
79+
button.centerYAnchor.constraint(equalTo: container.centerYAnchor),
80+
button.widthAnchor.constraint(equalToConstant: 16),
81+
button.heightAnchor.constraint(equalToConstant: 20),
82+
83+
popup.leadingAnchor.constraint(equalTo: button.trailingAnchor),
84+
popup.trailingAnchor.constraint(equalTo: container.trailingAnchor),
85+
popup.topAnchor.constraint(equalTo: container.topAnchor),
86+
popup.bottomAnchor.constraint(equalTo: container.bottomAnchor),
87+
popup.widthAnchor.constraint(equalToConstant: totalWidth)
88+
])
89+
90+
return container
91+
}
92+
93+
func updateNSView(_ nsView: NSView, context: Context) {
94+
if let popup = nsView.subviews.last as? NSPopUpButton {
95+
popup.selectItem(at: mode == .find ? 0 : 1)
96+
if let wrapItem = popup.menu?.items.last {
97+
wrapItem.state = wrapAround ? .on : .off
98+
}
99+
}
100+
101+
if let button = nsView.subviews.first as? NSButton {
102+
button.contentTintColor = activeState == .inactive ? .tertiaryLabelColor : .secondaryLabelColor
103+
}
104+
}
105+
106+
func makeCoordinator() -> Coordinator {
107+
Coordinator(self)
108+
}
109+
110+
var body: some View {
111+
let font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: .small))
112+
let maxWidth = FindPanelMode.allCases.map { mode in
113+
mode.displayName.size(withAttributes: [.font: font]).width
114+
}.max() ?? 0
115+
let totalWidth = maxWidth + 28 // Add padding for the chevron and spacing
116+
117+
return self.frame(width: totalWidth)
118+
}
119+
120+
class Coordinator: NSObject {
121+
let parent: FindModePicker
122+
123+
init(_ parent: FindModePicker) {
124+
self.parent = parent
125+
}
126+
127+
@objc func openMenu(_ sender: NSButton) {
128+
if let popup = sender.superview?.subviews.last as? NSPopUpButton {
129+
popup.performClick(nil)
130+
}
131+
}
132+
133+
@objc func modeSelected(_ sender: NSMenuItem) {
134+
parent.mode = sender.tag == 0 ? .find : .replace
135+
}
136+
137+
@objc func toggleWrapAround(_ sender: NSMenuItem) {
138+
parent.onToggleWrapAround()
139+
}
140+
}
141+
}

Sources/CodeEditSourceEditor/Find/PanelView/FindPanel.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ import Combine
1111

1212
// NSView wrapper for using SwiftUI view in AppKit
1313
final class FindPanel: NSView {
14-
static let height: CGFloat = 28
14+
/// The height of the find panel.
15+
static var height: CGFloat {
16+
if let findPanel = NSApp.windows.first(where: { $0.contentView is FindPanel })?.contentView as? FindPanel {
17+
return findPanel.viewModel.mode == .replace ? 56 : 28
18+
}
19+
return 28
20+
}
1521

1622
weak var findDelegate: FindPanelDelegate?
1723
private var hostingView: NSHostingView<FindPanelView>!

0 commit comments

Comments
 (0)