Skip to content

Commit

Permalink
Use dynamic NSColor for theme
Browse files Browse the repository at this point in the history
  • Loading branch information
1024jp committed Jul 11, 2024
1 parent 7e8e766 commit dc30128
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 30 deletions.
3 changes: 2 additions & 1 deletion CotEditor/Sources/EditorTextView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1387,8 +1387,9 @@ final class EditorTextView: NSTextView, Themable, CurrentLineHighlighting, Multi
for indicator in self.insertionIndicators {
indicator.color = self.insertionPointColor
}
self.selectedTextAttributes[.backgroundColor] = theme.effectiveSelectionColor(for: self.effectiveAppearance)
self.selectedTextAttributes[.backgroundColor] = theme.selectionColor
(self.layoutManager as? LayoutManager)?.invisiblesColor = theme.invisibles.color
(self.layoutManager as? LayoutManager)?.unemphasizedSelectedContentBackgroundColor = theme.unemphasizedSelectionColor

(self.window as? DocumentWindow)?.contentBackgroundColor = theme.background.color
self.enclosingScrollView?.backgroundColor = theme.background.color
Expand Down
14 changes: 4 additions & 10 deletions CotEditor/Sources/LayoutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class LayoutManager: NSLayoutManager, InvisibleDrawing, ValidationIgnorable, Lin
}

var invisiblesColor: NSColor = .disabledControlTextColor
var unemphasizedSelectedContentBackgroundColor: NSColor?

var showsIndentGuides = false
var tabWidth = 0
Expand Down Expand Up @@ -187,17 +188,10 @@ class LayoutManager: NSLayoutManager, InvisibleDrawing, ValidationIgnorable, Lin
// -> Otherwise, `.unemphasizedSelectedContentBackgroundColor` will be used forcibly and text becomes unreadable
// when the window appearance and theme are inconsistent.
if color == .unemphasizedSelectedContentBackgroundColor, // check if inactive
let textView = self.textContainer(forGlyphAt: self.glyphIndexForCharacter(at: charRange.location),
effectiveRange: nil, withoutAdditionalLayout: true)?.textView
let newColor = self.unemphasizedSelectedContentBackgroundColor,
newColor != color
{
MainActor.assumeIsolated {
if let theme = (textView as? any Themable)?.theme,
let newColor = theme.effectiveSecondarySelectionColor(for: textView.effectiveAppearance),
newColor != color
{
newColor.setFill()
}
}
newColor.setFill()
}

super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: charRange, color: color)
Expand Down
34 changes: 16 additions & 18 deletions CotEditor/Sources/Theme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -152,35 +152,33 @@ struct Theme: Equatable {
}


/// Returns the selection color to use with the given appearance.
///
/// - Parameter appearance: The current appearance of the text view to draw.
/// - Returns: A color.
func effectiveSelectionColor(for appearance: NSAppearance) -> NSColor {
/// The selection color to use.
var selectionColor: NSColor {

if self.selection.usesSystemSetting {
if self.isDarkTheme == appearance.isDark {
.selectedTextBackgroundColor
} else {
.selectedTextBackgroundColor.solve(for: appearance.appearance(for: self.isDarkTheme))
NSColor(name: nil) { [isDarkTheme = self.isDarkTheme] appearance in
if isDarkTheme == appearance.isDark {
.selectedTextBackgroundColor
} else {
.selectedTextBackgroundColor.solve(for: appearance.appearance(for: isDarkTheme))
}
}
} else {
self.selection.color
}
}


/// Returns the selection color to use for inactive views.
///
/// - Parameter appearance: The current appearance of the text view to draw.
/// - Returns: A color.
func effectiveSecondarySelectionColor(for appearance: NSAppearance) -> NSColor? {
/// The selection color to use for inactive views.
var unemphasizedSelectionColor: NSColor? {

if self.selection.usesSystemSetting {
return if self.isDarkTheme == appearance.isDark {
.unemphasizedSelectedContentBackgroundColor
} else {
.unemphasizedSelectedContentBackgroundColor.solve(for: appearance.appearance(for: self.isDarkTheme))
return NSColor(name: nil) { [isDarkTheme = self.isDarkTheme] appearance in
if isDarkTheme == appearance.isDark {
.unemphasizedSelectedContentBackgroundColor
} else {
.unemphasizedSelectedContentBackgroundColor.solve(for: appearance.appearance(for: isDarkTheme))
}
}
} else {
guard let color = self.selection.color.usingColorSpace(.genericRGB) else { return nil }
Expand Down
11 changes: 10 additions & 1 deletion Tests/ThemeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,18 @@ actor ThemeTests {
#expect(theme.invisibles.color.brightnessComponent.isApproximatelyEqual(to: 0.725, relativeTolerance: 0.01))
#expect(theme.background.color == NSColor.white.usingColorSpace(.genericRGB))
#expect(theme.lineHighlight.color.brightnessComponent.isApproximatelyEqual(to: 0.929, relativeTolerance: 0.01))
#expect(theme.effectiveSecondarySelectionColor(for: NSAppearance(named: .aqua)!) == .unemphasizedSelectedContentBackgroundColor)
#expect(!theme.isDarkTheme)

let aqua = try #require(NSAppearance(named: .aqua))
aqua.performAsCurrentDrawingAppearance {
#expect(theme.unemphasizedSelectionColor != nil)
}

let darkAppearance = try #require(NSAppearance(named: .darkAqua))
darkAppearance.performAsCurrentDrawingAppearance {
#expect(theme.unemphasizedSelectionColor != nil)
}

for type in SyntaxType.allCases {
let style = try #require(theme.style(for: type))
#expect(style.color.hueComponent > 0)
Expand Down

0 comments on commit dc30128

Please sign in to comment.