Skip to content

Commit 3529374

Browse files
Styling updates
1 parent 18e6b7e commit 3529374

File tree

4 files changed

+126
-35
lines changed

4 files changed

+126
-35
lines changed

CodeEdit/Features/NavigatorArea/IssueNavigator/OutlineView/IssueNavigatorViewController+NSOutlineViewDelegate.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ extension IssueNavigatorViewController: NSOutlineViewDelegate {
2424
guard let tableColumn else { return nil }
2525

2626
let frameRect = NSRect(x: 0, y: 0, width: tableColumn.width, height: rowHeight)
27-
let cell = StandardTableViewCell(frame: frameRect)
27+
2828
if let node = item as? (any IssueNode) {
29-
cell.configLabel(
30-
label: NSTextField(string: node.name),
31-
isEditable: false
32-
)
29+
let cell = IssueTableViewCell(frame: frameRect, node: node)
30+
return cell
3331
}
32+
33+
let cell = TextTableViewCell(frame: frameRect, startingText: "Unknown item")
3434
return cell
3535
}
3636

CodeEdit/Features/NavigatorArea/OutlineView/IssueTableViewCell.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,94 @@
55
// Created by Abe Malla on 3/16/25.
66
//
77

8+
import SwiftUI
9+
import AppKit
810

11+
class IssueTableViewCell: StandardTableViewCell {
12+
13+
private var nodeIconView: NSImageView?
14+
private var detailLabel: NSTextField?
15+
16+
var issueNode: (any IssueNode)?
17+
18+
/// Initializes the `IssueTableViewCell` with the issue node item
19+
/// - Parameters:
20+
/// - frameRect: The frame of the cell.
21+
/// - node: The issue node the cell represents.
22+
/// - isEditable: Set to true if the user should be able to edit the name (rarely used for issues).
23+
init(frame frameRect: NSRect, node: (any IssueNode)?, isEditable: Bool = false) {
24+
super.init(frame: frameRect, isEditable: isEditable)
25+
self.issueNode = node
26+
27+
secondaryLabelRightAligned = false
28+
configureForNode(node)
29+
}
30+
31+
override func configLabel(label: NSTextField, isEditable: Bool) {
32+
super.configLabel(label: label, isEditable: isEditable)
33+
label.lineBreakMode = .byTruncatingTail
34+
}
35+
36+
override func createIcon() -> NSImageView {
37+
let icon = super.createIcon()
38+
if let diagnosticNode = issueNode as? DiagnosticIssueNode {
39+
icon.contentTintColor = diagnosticNode.severityColor
40+
}
41+
return icon
42+
}
43+
44+
func configureForNode(_ node: (any IssueNode)?) {
45+
guard let node = node else { return }
46+
47+
textField?.stringValue = node.name
48+
49+
if let fileIssueNode = node as? FileIssueNode {
50+
imageView?.image = fileIssueNode.nsIcon
51+
} else if let diagnosticNode = node as? DiagnosticIssueNode {
52+
imageView?.image = diagnosticNode.icon
53+
imageView?.contentTintColor = diagnosticNode.severityColor
54+
}
55+
56+
if let diagnosticNode = node as? DiagnosticIssueNode {
57+
setupDetailLabel(with: diagnosticNode.locationString)
58+
} else if let projectNode = node as? ProjectIssueNode {
59+
let issuesCount = projectNode.errorCount + projectNode.warningCount
60+
61+
if issuesCount > 0 {
62+
secondaryLabel?.stringValue = "\(issuesCount) issues"
63+
}
64+
}
65+
}
66+
67+
private func setupDetailLabel(with text: String) {
68+
detailLabel?.removeFromSuperview()
69+
70+
let detail = NSTextField(labelWithString: text)
71+
detail.translatesAutoresizingMaskIntoConstraints = false
72+
detail.drawsBackground = false
73+
detail.isBordered = false
74+
detail.font = .systemFont(ofSize: fontSize-2)
75+
detail.textColor = .secondaryLabelColor
76+
77+
addSubview(detail)
78+
detailLabel = detail
79+
}
80+
81+
/// Returns the font size for the current row height. Defaults to `13.0`
82+
private var fontSize: Double {
83+
switch self.frame.height {
84+
case 20: return 11
85+
case 22: return 13
86+
case 24: return 14
87+
default: return 13
88+
}
89+
}
90+
91+
override init(frame frameRect: NSRect) {
92+
super.init(frame: frameRect)
93+
}
94+
95+
required init?(coder: NSCoder) {
96+
fatalError("init?(coder: NSCoder) isn't implemented on `IssueTableViewCell`.")
97+
}
98+
}

CodeEdit/Features/NavigatorArea/OutlineView/StandardTableViewCell.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class StandardTableViewCell: NSTableCellView {
2727
init(frame frameRect: NSRect, isEditable: Bool = true) {
2828
super.init(frame: frameRect)
2929
setupViews(frame: frameRect, isEditable: isEditable)
30-
3130
}
3231

3332
// Default init, assumes isEditable to be false

CodeEdit/Features/NavigatorArea/ViewModels/IssueNavigatorViewModel.swift

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ class IssueNavigatorViewModel: ObservableObject {
162162
protocol IssueNode: Identifiable, Hashable {
163163
var id: UUID { get }
164164
var name: String { get }
165-
var icon: Image { get }
166165
var isExpandable: Bool { get }
167166
}
168167

@@ -216,29 +215,32 @@ class FileIssueNode: IssueNode, ObservableObject {
216215
@Published var diagnostics: [DiagnosticIssueNode]
217216
@Published var isExpanded: Bool
218217

219-
// TODO: REPLACE WITH EXISTING CODE
220-
var icon: Image {
221-
// Determine icon based on file extension
222-
let fileExtension = (name as NSString).pathExtension.lowercased()
223-
224-
switch fileExtension {
225-
case "swift":
226-
return Image(systemName: "swift")
227-
case "js", "javascript":
228-
return Image(systemName: "j.square")
229-
case "html":
230-
return Image(systemName: "h.square")
231-
case "css":
232-
return Image(systemName: "c.square")
233-
case "md", "markdown":
234-
return Image(systemName: "doc.text")
235-
case "json":
236-
return Image(systemName: "curlybraces")
237-
case "txt":
238-
return Image(systemName: "doc.text")
239-
default:
240-
return Image(systemName: "doc")
218+
/// Returns the extension of the file or an empty string if no extension is present.
219+
var type: FileIcon.FileType {
220+
let filename = (uri as NSString).pathExtension
221+
let fileExtension = (filename as NSString).pathExtension.lowercased()
222+
223+
if !fileExtension.isEmpty {
224+
if let type = FileIcon.FileType(rawValue: fileExtension) {
225+
return type
226+
}
241227
}
228+
if let type = FileIcon.FileType(rawValue: filename.lowercased()) {
229+
return type
230+
}
231+
return .txt
232+
}
233+
234+
/// Return the icon of the file as `Image`
235+
var icon: Image {
236+
return Image(systemName: FileIcon.fileIcon(fileType: type))
237+
}
238+
239+
/// Return the icon of the file as `NSImage`
240+
var nsIcon: NSImage {
241+
let systemImage = FileIcon.fileIcon(fileType: type)
242+
return NSImage(systemSymbolName: systemImage, accessibilityDescription: systemImage)
243+
?? NSImage(systemSymbolName: "doc", accessibilityDescription: "doc")!
242244
}
243245

244246
var isExpandable: Bool {
@@ -283,18 +285,18 @@ class DiagnosticIssueNode: IssueNode, ObservableObject {
283285
false
284286
}
285287

286-
var icon: Image {
288+
var icon: NSImage {
287289
switch diagnostic.severity {
288290
case .error:
289-
return Image(systemName: "exclamationmark.circle.fill")
291+
return NSImage(systemSymbolName: "exclamationmark.octagon.fill", accessibilityDescription: "")!
290292
case .warning:
291-
return Image(systemName: "exclamationmark.triangle.fill")
293+
return NSImage(systemSymbolName: "exclamationmark.triangle.fill", accessibilityDescription: "")!
292294
case .information:
293-
return Image(systemName: "info.circle.fill")
295+
return NSImage(systemSymbolName: "exclamationmark.triangle.fill", accessibilityDescription: "")!
294296
case .hint:
295-
return Image(systemName: "lightbulb.fill")
297+
return NSImage(systemSymbolName: "lightbulb.fill", accessibilityDescription: "")!
296298
case nil:
297-
return Image(systemName: "circle.fill")
299+
return NSImage(systemSymbolName: "circle.fill", accessibilityDescription: "")!
298300
}
299301
}
300302

0 commit comments

Comments
 (0)