Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Divyesh/feature/support undo redo #86

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix redo and undo for color attrubutes
  • Loading branch information
cp-divyesh-v committed Jan 13, 2025
commit bc7b8a90024a1d5f844d05c2331294f1994bc436
5 changes: 3 additions & 2 deletions Sources/RichEditorSwiftUI/Data/Models/RichAttributes.swift
Original file line number Diff line number Diff line change
@@ -196,9 +196,10 @@ extension RichAttributes {
size: (att.size != nil ? (byAdding ? att.size! : nil) : self.size),
font: (att.font != nil ? (byAdding ? att.font! : nil) : self.font),
color: (att.color != nil
? (byAdding ? att.color! : nil) : self.color),
? (byAdding ? att.color! : nil) : (att.color == nil && !byAdding) ? nil : self.color),
background: (att.background != nil
? (byAdding ? att.background! : nil) : self.background),
? (byAdding ? att.background! : nil)
: (att.background == nil && !byAdding) ? nil : self.background),
align: (att.align != nil
? (byAdding ? att.align! : nil) : self.align),
///nil link indicates removal as well so removing link if `byAdding == false && att.link == nil`
2 changes: 2 additions & 0 deletions Sources/RichEditorSwiftUI/UI/Context/RichEditorState.swift
Original file line number Diff line number Diff line change
@@ -108,6 +108,8 @@ public class RichEditorState: ObservableObject {
public internal(set) var colors = [RichTextColor: ColorRepresentable]() {
willSet { previousColors = colors }
}
@Published
internal var colorScheme: ColorScheme = .light

/// The style to apply when highlighting a range.
@Published
91 changes: 45 additions & 46 deletions Sources/RichEditorSwiftUI/UI/Context/RichTextContext+Color.swift
Original file line number Diff line number Diff line change
@@ -9,59 +9,58 @@ import SwiftUI

extension RichEditorState {

/// Get a binding for a certain color.
public func binding(for color: RichTextColor) -> Binding<Color> {
Binding(
get: { Color(self.color(for: color) ?? .clear) },
set: { self.updateStyleFor(color, to: .init($0)) }
)
}
/// Get a binding for a certain color.
public func binding(for color: RichTextColor) -> Binding<Color> {
Binding(
get: { Color(self.color(for: color) ?? .clear) },
set: { self.updateStyleFor(color, to: .init($0)) }
)
}

/// Get the value for a certain color.
public func color(for color: RichTextColor) -> ColorRepresentable? {
colors[color]
}
/// Get the value for a certain color.
public func color(for color: RichTextColor) -> ColorRepresentable? {
colors[color]
}

/// Set the value for a certain color.
public func setColor(
_ color: RichTextColor,
to val: ColorRepresentable
) {
guard self.color(for: color) != val else { return }
actionPublisher.send(.setColor(color, val))
setColorInternal(color, to: val)
}
/// Set the value for a certain color.
public func setColor(
_ color: RichTextColor,
to val: ColorRepresentable
) {
actionPublisher.send(.setColor(color, val))
setColorInternal(color, to: val)
}

public func updateStyleFor(
_ color: RichTextColor, to val: ColorRepresentable
) {
let value = Color(val)
switch color {
case .foreground:
updateStyle(style: .color(value))
case .background:
updateStyle(style: .background(value))
case .strikethrough:
return
case .stroke:
return
case .underline:
return
}
public func updateStyleFor(
_ color: RichTextColor, to val: ColorRepresentable
) {
let value = Color(val)
switch color {
case .foreground:
updateStyle(style: .color(value))
case .background:
updateStyle(style: .background(value))
case .strikethrough:
return
case .stroke:
return
case .underline:
return
}
}
}

extension RichEditorState {

/// Set the value for a certain color, or remove it.
func setColorInternal(
_ color: RichTextColor,
to val: ColorRepresentable?
) {
guard let val else {
colors[color] = nil
return
}
colors[color] = val
/// Set the value for a certain color, or remove it.
func setColorInternal(
_ color: RichTextColor,
to val: ColorRepresentable?
) {
guard let val else {
colors[color] = nil
return
}
colors[color] = val
}
}
7 changes: 7 additions & 0 deletions Sources/RichEditorSwiftUI/UI/Editor/RichEditor.swift
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@
/// For more information, see ``RichTextKeyboardToolbarConfig``
/// and ``RichTextKeyboardToolbarStyle``.
public struct RichTextEditor: ViewRepresentable {
@Environment(\.colorScheme) var colorScheme

@State var cancellable: Set<AnyCancellable> = []

@@ -112,6 +113,9 @@
textView.configuration = config
textView.theme = style
viewConfiguration(textView)
DispatchQueue.main.async {
self.context.colorScheme = self.colorScheme
}
return textView
}

@@ -129,6 +133,9 @@
textView.configuration = config
textView.theme = style
viewConfiguration(textView)
DispatchQueue.main.async {
self.context.colorScheme = self.colorScheme
}
return scrollView
}

22 changes: 16 additions & 6 deletions Sources/RichEditorSwiftUI/UI/Editor/RichEditorState+Spans.swift
Original file line number Diff line number Diff line change
@@ -67,9 +67,9 @@ extension RichEditorState {
- style: is of type RichTextSpanStyle
*/
public func updateStyle(style: RichTextSpanStyle, shouldRegisterUndo: Bool = true) {
let wasStyleActive = activeStyles.contains(style)
setInternalStyles(style: style)
setStyle(style, shouldRegisterUndo: shouldRegisterUndo)
/// Don't change order of function call as it is comparing active attributes with new one so updating it before applying attribute will break the behavior of undo and redo
setInternalStyles(style: style)
}
}

@@ -180,19 +180,21 @@ extension RichEditorState {
addStyle = fontName == self.fontName
}
case .color(let color):
if let color, color.toHex() != Color.primary.toHex() {
let defaultColor = RichTextColor.foreground.adjust(nil, for: colorScheme)
if let color, color.toHex() != defaultColor.toHex() {
if let internalColor = self.color(for: .foreground) {
addStyle = Color(internalColor) != color
addStyle = (Color(internalColor) != color)
} else {
addStyle = true
}
} else {
addStyle = false
}
case .background(let bgColor):
if let color = bgColor, color.toHex() != Color.clear.toHex() {
let defaultColor = RichTextColor.background.adjust(nil, for: colorScheme)
if let color = bgColor, color.toHex() != defaultColor.toHex() {
if let internalColor = self.color(for: .background) {
addStyle = Color(internalColor) != color
addStyle = (Color(internalColor) == color)
} else {
addStyle = true
}
@@ -899,10 +901,18 @@ extension RichEditorState {
case .color(let color):
if let color {
setColor(.foreground, to: .init(color))
} else {
setColor(
.foreground,
to: ColorRepresentable(RichTextColor.foreground.adjust(nil, for: colorScheme)))
}
case .background(let color):
if let color {
setColor(.background, to: .init(color))
} else {
setColor(
.background,
to: ColorRepresentable(RichTextColor.background.adjust(nil, for: colorScheme)))
}
case .align(let alignment):
if let alignment, alignment != self.textAlignment {