diff --git a/Sources/Core/View/TextEditor.swift b/Sources/Core/View/TextEditor.swift index ca6d295..e848e74 100644 --- a/Sources/Core/View/TextEditor.swift +++ b/Sources/Core/View/TextEditor.swift @@ -14,18 +14,82 @@ public typealias TextEditor = TextView public struct TextView: AdwaitaWidget { /// The editor's content. - @Binding var text: String + @BindingProperty( + observe: { _, text, storage in + if let buffer = storage.content["buffer"]?.first { + buffer.connectSignal(name: "changed") { + let currentText = Self.getText(buffer: buffer) + if text.wrappedValue != currentText { + text.wrappedValue = currentText + } + } + } + }, + set: { _, text, storage in + if let buffer = storage.content["buffer"]?.first, Self.getText(buffer: buffer) != text { + gtk_text_buffer_set_text(buffer.opaquePointer?.cast(), text, -1) + } + }, + pointer: Any.self + ) + var text: Binding = .constant("") /// The padding between the border and the content. - var padding = 0 - /// The edges affected by the padding. - var paddingEdges: Set = [] + @Property( + set: { $1.set($0) }, + pointer: OpaquePointer.self + ) + var padding: InnerPadding? /// The (word) wrap mode used when rendering the text. + @Property( + set: { gtk_text_view_set_wrap_mode($0.cast(), $1.rawValue) }, + pointer: OpaquePointer.self + ) var wrapMode: WrapMode = .none /// Initialize a text editor. /// - Parameter text: The editor's content. public init(text: Binding) { - self._text = text + self.text = text + } + + /// The inner padding of a text view. + struct InnerPadding { + + /// The padding. + var padding: Int + /// The affected edges. + var paddingEdges: Set + + /// Set the inner padding on a text view. + /// - Parameter pointer: The text view. + func set(_ pointer: OpaquePointer) { + if paddingEdges.contains(.top) { + gtk_text_view_set_top_margin(pointer.cast(), padding.cInt) + } + if paddingEdges.contains(.bottom) { + gtk_text_view_set_bottom_margin(pointer.cast(), padding.cInt) + } + if paddingEdges.contains(.leading) { + gtk_text_view_set_left_margin(pointer.cast(), padding.cInt) + } + if paddingEdges.contains(.trailing) { + gtk_text_view_set_right_margin(pointer.cast(), padding.cInt) + } + } + + } + + /// Get the text view's content. + /// - Parameter buffer: The text view's buffer. + /// - Returns: The content. + static func getText(buffer: ViewStorage) -> String { + let startIter: UnsafeMutablePointer = .allocate(capacity: 1) + let endIter: UnsafeMutablePointer = .allocate(capacity: 1) + gtk_text_buffer_get_start_iter(buffer.opaquePointer?.cast(), startIter) + gtk_text_buffer_get_end_iter(buffer.opaquePointer?.cast(), endIter) + return .init( + cString: gtk_text_buffer_get_text(buffer.opaquePointer?.cast(), startIter, endIter, true.cBool) + ) } /// Get the editor's view storage. @@ -33,54 +97,17 @@ public struct TextView: AdwaitaWidget { /// - data: The widget data. /// - type: The view render data type. /// - Returns: The view storage. - public func container(data: WidgetData, type: Data.Type) -> ViewStorage { + public func container(data: WidgetData, type: Data.Type) -> ViewStorage where Data: ViewRenderData { let buffer = ViewStorage(gtk_text_buffer_new(nil)?.opaque()) let editor = ViewStorage( gtk_text_view_new_with_buffer(buffer.opaquePointer?.cast())?.opaque(), content: ["buffer": [buffer]] ) + initProperties(editor, data: data, type: type) update(editor, data: data, updateProperties: true, type: type) return editor } - /// Update a view storage to the editor. - /// - Parameters: - /// - storage: The view storage. - /// - data: The widget data. - /// - updateProperties: Whether to update the view's properties. - /// - type: The view render data type. - public func update(_ storage: ViewStorage, data: WidgetData, updateProperties: Bool, type: Data.Type) { - if let buffer = storage.content["buffer"]?.first { - buffer.connectSignal(name: "changed") { - let text = getText(buffer: buffer) - if self.text != text { - self.text = text - } - } - if updateProperties { - if getText(buffer: buffer) != self.text { - gtk_text_buffer_set_text(buffer.opaquePointer?.cast(), text, -1) - } - } - } - if updateProperties { - if paddingEdges.contains(.top) { - gtk_text_view_set_top_margin(storage.opaquePointer?.cast(), padding.cInt) - } - if paddingEdges.contains(.bottom) { - gtk_text_view_set_bottom_margin(storage.opaquePointer?.cast(), padding.cInt) - } - if paddingEdges.contains(.leading) { - gtk_text_view_set_left_margin(storage.opaquePointer?.cast(), padding.cInt) - } - if paddingEdges.contains(.trailing) { - gtk_text_view_set_right_margin(storage.opaquePointer?.cast(), padding.cInt) - } - // update the wrapping mode - gtk_text_view_set_wrap_mode(storage.opaquePointer?.cast(), wrapMode.rawValue) - } - } - /// Set the wrapMode for the text view. /// /// Available wrap modes are `none`, `char`, `word`, `wordChar`. Please refer to the @@ -93,19 +120,6 @@ public struct TextView: AdwaitaWidget { return newSelf } - /// Get the text view's content. - /// - Parameter buffer: The text view's buffer. - /// - Returns: The content. - func getText(buffer: ViewStorage) -> String { - let startIter: UnsafeMutablePointer = .allocate(capacity: 1) - let endIter: UnsafeMutablePointer = .allocate(capacity: 1) - gtk_text_buffer_get_start_iter(buffer.opaquePointer?.cast(), startIter) - gtk_text_buffer_get_end_iter(buffer.opaquePointer?.cast(), endIter) - return .init( - cString: gtk_text_buffer_get_text(buffer.opaquePointer?.cast(), startIter, endIter, true.cBool) - ) - } - /// Add padding between the editor's content and border. /// - Parameters: /// - padding: The padding's value. @@ -113,8 +127,7 @@ public struct TextView: AdwaitaWidget { /// - Returns: The editor. public func innerPadding(_ padding: Int = 10, edges: Set = .all) -> Self { var newSelf = self - newSelf.padding = padding - newSelf.paddingEdges = edges + newSelf.padding = .init(padding: padding, paddingEdges: edges) return newSelf }