From 0666597e29f6e85a4018539342a374dc9285b217 Mon Sep 17 00:00:00 2001 From: Igor Marcossi Date: Tue, 2 Jul 2024 03:09:22 -0300 Subject: [PATCH] =?UTF-8?q?Adds=20`backgroundColor`=20parameter=20to=20ove?= =?UTF-8?q?rride=20theme=E2=80=99s=20default.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/CodeEditor/CodeEditor.swift | 220 +++++++++--------- Sources/CodeEditor/UXCodeTextView.swift | 6 +- .../UXCodeTextViewRepresentable.swift | 112 ++++----- 3 files changed, 177 insertions(+), 161 deletions(-) diff --git a/Sources/CodeEditor/CodeEditor.swift b/Sources/CodeEditor/CodeEditor.swift index a0b87cb..e88f87c 100644 --- a/Sources/CodeEditor/CodeEditor.swift +++ b/Sources/CodeEditor/CodeEditor.swift @@ -205,128 +205,136 @@ public struct CodeEditor: View { * Configures a CodeEditor View with the given parameters. * * - Parameters: - * - source: A binding to a String that holds the source code to be - * edited (or displayed). - * - selection: A binding to the selected range of the `source`. - * - language: Optionally set a language (e.g. `.swift`), otherwise - * Highlight.js will attempt to detect the language. - * - theme: The name of the theme to use, defaults to "pojoaque". - * - fontSize: On macOS this Binding can be used to persist the size of - * the font in use. At runtime this is combined with the - * theme to produce the full font information. (optional) - * - flags: Configure whether the text is editable and/or selectable - * (defaults to both). - * - indentStyle: Optionally insert a configurable amount of spaces if the - * user hits "tab". - * - autoPairs: A mapping of open/close characters, where the close - * characters are automatically injected when the user enters - * the opening character. For example: `[ "{": "}" ]` would - * automatically insert the closing "}" if the user enters - * "{". If no value is given, the default mapping for the - * language is used. - * - inset: The editor can be inset in the scroll view. Defaults to - * 8/8. - * - autoscroll: If enabled, the editor automatically scrolls to the respective - * region when the `selection` is changed programatically. + * - source: A binding to a String that holds the source code to be + * edited (or displayed). + * - selection: A binding to the selected range of the `source`. + * - language: Optionally set a language (e.g. `.swift`), otherwise + * Highlight.js will attempt to detect the language. + * - theme: The name of the theme to use, defaults to "pojoaque". + * - fontSize: On macOS this Binding can be used to persist the size of + * the font in use. At runtime this is combined with the + * theme to produce the full font information. (optional) + * - flags: Configure whether the text is editable and/or selectable + * (defaults to both). + * - indentStyle: Optionally insert a configurable amount of spaces if the + * user hits "tab". + * - autoPairs: A mapping of open/close characters, where the close + * characters are automatically injected when the user enters + * the opening character. For example: `[ "{": "}" ]` would + * automatically insert the closing "}" if the user enters + * "{". If no value is given, the default mapping for the + * language is used. + * - inset: The editor can be inset in the scroll view. Defaults to + * 8/8. + * - autoscroll: If enabled, the editor automatically scrolls to the respective + * region when the `selection` is changed programatically. + * - backgroundColor: Overrides theme's background color. */ - public init(source : Binding, - selection : Binding>? = nil, - language : Language? = nil, - theme : ThemeName = .default, - fontSize : Binding? = nil, - flags : Flags = .defaultEditorFlags, - indentStyle : IndentStyle = .system, - autoPairs : [ String : String ]? = nil, - inset : CGSize? = nil, - allowsUndo : Bool = true, - autoscroll : Bool = true) + public init(source : Binding, + selection : Binding>? = nil, + language : Language? = nil, + theme : ThemeName = .default, + fontSize : Binding? = nil, + flags : Flags = .defaultEditorFlags, + indentStyle : IndentStyle = .system, + autoPairs : [ String : String ]? = nil, + inset : CGSize? = nil, + allowsUndo : Bool = true, + autoscroll : Bool = true, + backgroundColor: NSColor? = nil) { - self.source = source - self.selection = selection - self.fontSize = fontSize - self.language = language - self.themeName = theme - self.flags = flags - self.indentStyle = indentStyle - self.inset = inset ?? CGSize(width: 8, height: 8) - self.autoPairs = autoPairs - ?? language.flatMap({ CodeEditor.defaultAutoPairs[$0] }) - ?? [:] - self.allowsUndo = allowsUndo - self.autoscroll = autoscroll + self.source = source + self.selection = selection + self.fontSize = fontSize + self.language = language + self.themeName = theme + self.flags = flags + self.indentStyle = indentStyle + self.inset = inset ?? CGSize(width: 8, height: 8) + self.autoPairs = autoPairs + ?? language.flatMap({ CodeEditor.defaultAutoPairs[$0] }) + ?? [:] + self.allowsUndo = allowsUndo + self.autoscroll = autoscroll + self.backgroundColor = backgroundColor } /** * Configures a read-only CodeEditor View with the given parameters. * * - Parameters: - * - source: A String that holds the source code to be displayed. - * - language: Optionally set a language (e.g. `.swift`), otherwise - * Highlight.js will attempt to detect the language. - * - theme: The name of the theme to use, defaults to "pojoaque". - * - fontSize: On macOS this Binding can be used to persist the size of - * the font in use. At runtime this is combined with the - * theme to produce the full font information. (optional) - * - flags: Configure whether the text is selectable - * (defaults to both). - * - indentStyle: Optionally insert a configurable amount of spaces if the - * user hits "tab". - * - autoPairs: A mapping of open/close characters, where the close - * characters are automatically injected when the user enters - * the opening character. For example: `[ "{": "}" ]` would - * automatically insert the closing "}" if the user enters - * "{". If no value is given, the default mapping for the - * language is used. - * - inset: The editor can be inset in the scroll view. Defaults to - * 8/8. + * - source: A String that holds the source code to be displayed. + * - language: Optionally set a language (e.g. `.swift`), otherwise + * Highlight.js will attempt to detect the language. + * - theme: The name of the theme to use, defaults to "pojoaque". + * - fontSize: On macOS this Binding can be used to persist the size of + * the font in use. At runtime this is combined with the + * theme to produce the full font information. (optional) + * - flags: Configure whether the text is selectable + * (defaults to both). + * - indentStyle: Optionally insert a configurable amount of spaces if the + * user hits "tab". + * - autoPairs: A mapping of open/close characters, where the close + * characters are automatically injected when the user enters + * the opening character. For example: `[ "{": "}" ]` would + * automatically insert the closing "}" if the user enters + * "{". If no value is given, the default mapping for the + * language is used. + * - inset: The editor can be inset in the scroll view. Defaults to + * 8/8. + * - backgroundColor: Overrides theme's background color. */ @inlinable - public init(source : String, - language : Language? = nil, - theme : ThemeName = .default, - fontSize : Binding? = nil, - flags : Flags = .defaultViewerFlags, - indentStyle : IndentStyle = .system, - autoPairs : [ String : String ]? = nil, - inset : CGSize? = nil, - allowsUndo : Bool = true) + public init(source : String, + language : Language? = nil, + theme : ThemeName = .default, + fontSize : Binding? = nil, + flags : Flags = .defaultViewerFlags, + indentStyle : IndentStyle = .system, + autoPairs : [ String : String ]? = nil, + inset : CGSize? = nil, + allowsUndo : Bool = true, + backgroundColor: NSColor? = nil) { assert(!flags.contains(.editable), "Editing requires a Binding") - self.init(source : .constant(source), - language : language, - theme : theme, - fontSize : fontSize, - flags : flags.subtracting(.editable), - indentStyle : indentStyle, - autoPairs : autoPairs, - inset : inset, - allowsUndo : allowsUndo) + self.init(source : .constant(source), + language : language, + theme : theme, + fontSize : fontSize, + flags : flags.subtracting(.editable), + indentStyle : indentStyle, + autoPairs : autoPairs, + inset : inset, + allowsUndo : allowsUndo, + backgroundColor: backgroundColor) } - private var source : Binding - private var selection : Binding>? - private var fontSize : Binding? - private let language : Language? - private let themeName : ThemeName - private let flags : Flags - private let indentStyle : IndentStyle - private let autoPairs : [ String : String ] - private let inset : CGSize - private let allowsUndo : Bool - private let autoscroll : Bool + private var source : Binding + private var selection : Binding>? + private var fontSize : Binding? + private let language : Language? + private let themeName : ThemeName + private let flags : Flags + private let indentStyle : IndentStyle + private let autoPairs : [ String : String ] + private let inset : CGSize + private let allowsUndo : Bool + private let autoscroll : Bool + private let backgroundColor : NSColor? public var body: some View { - UXCodeTextViewRepresentable(source : source, - selection : selection, - language : language, - theme : themeName, - fontSize : fontSize, - flags : flags, - indentStyle : indentStyle, - autoPairs : autoPairs, - inset : inset, - allowsUndo : allowsUndo, - autoscroll : autoscroll) + UXCodeTextViewRepresentable(source : source, + selection : selection, + language : language, + theme : themeName, + fontSize : fontSize, + flags : flags, + indentStyle : indentStyle, + autoPairs : autoPairs, + inset : inset, + allowsUndo : allowsUndo, + autoscroll : autoscroll, + backgroundColor: backgroundColor) } } diff --git a/Sources/CodeEditor/UXCodeTextView.swift b/Sources/CodeEditor/UXCodeTextView.swift index 22b2193..7c680ae 100644 --- a/Sources/CodeEditor/UXCodeTextView.swift +++ b/Sources/CodeEditor/UXCodeTextView.swift @@ -29,6 +29,8 @@ import Highlightr final class UXCodeTextView: UXTextView { fileprivate let highlightr = Highlightr() + + var customBackgroundColor: NSColor? = nil private var hlTextStorage : CodeAttributedString? { return textStorage as? CodeAttributedString @@ -245,7 +247,7 @@ final class UXCodeTextView: UXTextView { guard let highlightr = highlightr, highlightr.setTheme(to: newTheme.rawValue), let theme = highlightr.theme else { return false } - self.backgroundColor = theme.themeBackgroundColor + self.backgroundColor = customBackgroundColor ?? theme.themeBackgroundColor if let font = theme.codeFont, font !== self.font { self.font = font } return true } @@ -264,7 +266,7 @@ final class UXCodeTextView: UXTextView { theme.codeFont = theme.codeFont? .withSize(newSize) theme.boldCodeFont = theme.boldCodeFont? .withSize(newSize) theme.italicCodeFont = theme.italicCodeFont?.withSize(newSize) - self.backgroundColor = theme.themeBackgroundColor + self.backgroundColor = customBackgroundColor ?? theme.themeBackgroundColor if let font = theme.codeFont, font !== self.font { self.font = font } return true } diff --git a/Sources/CodeEditor/UXCodeTextViewRepresentable.swift b/Sources/CodeEditor/UXCodeTextViewRepresentable.swift index 050aeb2..d9a1321 100644 --- a/Sources/CodeEditor/UXCodeTextViewRepresentable.swift +++ b/Sources/CodeEditor/UXCodeTextViewRepresentable.swift @@ -23,63 +23,67 @@ struct UXCodeTextViewRepresentable : UXViewRepresentable { * Configures a CodeEditor View with the given parameters. * * - Parameters: - * - source: A binding to a String that holds the source code to be - * edited (or displayed). - * - language: Optionally set a language (e.g. `.swift`), otherwise - * Highlight.js will attempt to detect the language. - * - theme: The name of the theme to use. - * - fontSize: On macOS this Binding can be used to persist the size of - * the font in use. At runtime this is combined with the - * theme to produce the full font information. - * - flags: Configure whether the text is editable and/or selectable. - * - indentStyle: Optionally insert a configurable amount of spaces if the - * user hits "tab". - * - inset: The editor can be inset in the scroll view. Defaults to - * 8/8. - * - autoPairs: A mapping of open/close characters, where the close - * characters are automatically injected when the user enters - * the opening character. For example: `[ "<": ">" ]` would - * automatically insert the closing ">" if the user enters - * "<". - * - autoscroll: If enabled, the editor automatically scrolls to the respective - * region when the `selection` is changed programatically. + * - source: A binding to a String that holds the source code to be + * edited (or displayed). + * - language: Optionally set a language (e.g. `.swift`), otherwise + * Highlight.js will attempt to detect the language. + * - theme: The name of the theme to use. + * - fontSize: On macOS this Binding can be used to persist the size of + * the font in use. At runtime this is combined with the + * theme to produce the full font information. + * - flags: Configure whether the text is editable and/or selectable. + * - indentStyle: Optionally insert a configurable amount of spaces if the + * user hits "tab". + * - inset: The editor can be inset in the scroll view. Defaults to + * 8/8. + * - autoPairs: A mapping of open/close characters, where the close + * characters are automatically injected when the user enters + * the opening character. For example: `[ "<": ">" ]` would + * automatically insert the closing ">" if the user enters + * "<". + * - autoscroll: If enabled, the editor automatically scrolls to the respective + * region when the `selection` is changed programatically. + * - backgroundColor: Overrides theme's background color. */ - public init(source : Binding, - selection : Binding>?, - language : CodeEditor.Language?, - theme : CodeEditor.ThemeName, - fontSize : Binding?, - flags : CodeEditor.Flags, - indentStyle : CodeEditor.IndentStyle, - autoPairs : [ String : String ], - inset : CGSize, - allowsUndo : Bool, - autoscroll : Bool) + public init(source : Binding, + selection : Binding>?, + language : CodeEditor.Language?, + theme : CodeEditor.ThemeName, + fontSize : Binding?, + flags : CodeEditor.Flags, + indentStyle : CodeEditor.IndentStyle, + autoPairs : [ String : String ], + inset : CGSize, + allowsUndo : Bool, + autoscroll : Bool, + backgroundColor: NSColor? = nil) { - self.source = source - self.selection = selection - self.fontSize = fontSize - self.language = language - self.themeName = theme - self.flags = flags - self.indentStyle = indentStyle - self.autoPairs = autoPairs - self.inset = inset - self.allowsUndo = allowsUndo - self.autoscroll = autoscroll + self.source = source + self.selection = selection + self.fontSize = fontSize + self.language = language + self.themeName = theme + self.flags = flags + self.indentStyle = indentStyle + self.autoPairs = autoPairs + self.inset = inset + self.allowsUndo = allowsUndo + self.autoscroll = autoscroll + self.customBackgroundColor = backgroundColor } - private var source : Binding - private var selection : Binding>? - private var fontSize : Binding? - private let language : CodeEditor.Language? - private let themeName : CodeEditor.ThemeName - private let flags : CodeEditor.Flags - private let indentStyle : CodeEditor.IndentStyle - private let inset : CGSize - private let allowsUndo : Bool - private let autoPairs : [ String : String ] - private let autoscroll : Bool + private var source : Binding + private var selection : Binding>? + private var fontSize : Binding? + private var customBackgroundColor : NSColor? = nil + private let language : CodeEditor.Language? + private let themeName : CodeEditor.ThemeName + private let flags : CodeEditor.Flags + private let indentStyle : CodeEditor.IndentStyle + private let inset : CGSize + private let allowsUndo : Bool + private let autoPairs : [ String : String ] + private let autoscroll : Bool // The inner `value` is true, exactly when execution is inside // the `updateTextView(_:)` method. The `Coordinator` can use this @@ -236,6 +240,7 @@ struct UXCodeTextViewRepresentable : UXViewRepresentable { #if os(macOS) public func makeNSView(context: Context) -> NSScrollView { let textView = UXCodeTextView() + textView.customBackgroundColor = customBackgroundColor textView.autoresizingMask = [ .width, .height ] textView.delegate = context.coordinator textView.allowsUndo = allowsUndo @@ -257,6 +262,7 @@ struct UXCodeTextViewRepresentable : UXViewRepresentable { if textView.delegate !== context.coordinator { textView.delegate = context.coordinator } + textView.customBackgroundColor = customBackgroundColor textView.textContainerInset = inset updateTextView(textView) }