mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-18 20:30:51 +00:00
Proper toolbar
This commit is contained in:
parent
6553d1f34f
commit
4adcbe99ad
@ -843,6 +843,7 @@ private enum SGDebugActions: String {
|
|||||||
private enum SGDebugToggles: String {
|
private enum SGDebugToggles: String {
|
||||||
case forceImmediateShareSheet
|
case forceImmediateShareSheet
|
||||||
case legacyNotificationsFix
|
case legacyNotificationsFix
|
||||||
|
case inputToolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -860,6 +861,9 @@ private func SGDebugControllerEntries(presentationData: PresentationData) -> [SG
|
|||||||
if SGSimpleSettings.shared.b {
|
if SGSimpleSettings.shared.b {
|
||||||
entries.append(.disclosure(id: id.count, section: .base, link: .sessionBackupManager, text: "Session Backup"))
|
entries.append(.disclosure(id: id.count, section: .base, link: .sessionBackupManager, text: "Session Backup"))
|
||||||
entries.append(.disclosure(id: id.count, section: .base, link: .messageFilter, text: "Message Filter"))
|
entries.append(.disclosure(id: id.count, section: .base, link: .messageFilter, text: "Message Filter"))
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
entries.append(.toggle(id: id.count, section: .base, settingName: .inputToolbar, value: SGSimpleSettings.shared.inputToolbar, text: "Message Formatting Toolbar", enabled: true))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
entries.append(.action(id: id.count, section: .base, actionType: .clearRegDateCache, text: "Clear Regdate cache", kind: .generic))
|
entries.append(.action(id: id.count, section: .base, actionType: .clearRegDateCache, text: "Clear Regdate cache", kind: .generic))
|
||||||
entries.append(.toggle(id: id.count, section: .base, settingName: .forceImmediateShareSheet, value: SGSimpleSettings.shared.forceSystemSharing, text: "Force System Share Sheet", enabled: true))
|
entries.append(.toggle(id: id.count, section: .base, settingName: .forceImmediateShareSheet, value: SGSimpleSettings.shared.forceSystemSharing, text: "Force System Share Sheet", enabled: true))
|
||||||
@ -884,6 +888,8 @@ public func sgDebugController(context: AccountContext) -> ViewController {
|
|||||||
SGSimpleSettings.shared.forceSystemSharing = value
|
SGSimpleSettings.shared.forceSystemSharing = value
|
||||||
case .legacyNotificationsFix:
|
case .legacyNotificationsFix:
|
||||||
SGSimpleSettings.shared.legacyNotificationsFix = value
|
SGSimpleSettings.shared.legacyNotificationsFix = value
|
||||||
|
case .inputToolbar:
|
||||||
|
SGSimpleSettings.shared.inputToolbar = value
|
||||||
}
|
}
|
||||||
}, openDisclosureLink: { link in
|
}, openDisclosureLink: { link in
|
||||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|||||||
@ -34,7 +34,8 @@ public class SGSimpleSettings {
|
|||||||
{ let _ = self.disableSendAsButton },
|
{ let _ = self.disableSendAsButton },
|
||||||
{ let _ = self.disableSnapDeletionEffect },
|
{ let _ = self.disableSnapDeletionEffect },
|
||||||
{ let _ = self.startTelescopeWithRearCam },
|
{ let _ = self.startTelescopeWithRearCam },
|
||||||
{ let _ = self.hideRecordingButton }
|
{ let _ = self.hideRecordingButton },
|
||||||
|
{ let _ = self.inputToolbar }
|
||||||
]
|
]
|
||||||
|
|
||||||
tasks.forEach { task in
|
tasks.forEach { task in
|
||||||
@ -109,6 +110,7 @@ public class SGSimpleSettings {
|
|||||||
case videoPIPSwipeDirection
|
case videoPIPSwipeDirection
|
||||||
case legacyNotificationsFix
|
case legacyNotificationsFix
|
||||||
case messageFilterKeywords
|
case messageFilterKeywords
|
||||||
|
case inputToolbar
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DownloadSpeedBoostValues: String, CaseIterable {
|
public enum DownloadSpeedBoostValues: String, CaseIterable {
|
||||||
@ -207,7 +209,8 @@ public class SGSimpleSettings {
|
|||||||
Keys.confirmCalls.rawValue: true,
|
Keys.confirmCalls.rawValue: true,
|
||||||
Keys.videoPIPSwipeDirection.rawValue: VideoPIPSwipeDirection.up.rawValue,
|
Keys.videoPIPSwipeDirection.rawValue: VideoPIPSwipeDirection.up.rawValue,
|
||||||
Keys.legacyNotificationsFix.rawValue: false,
|
Keys.legacyNotificationsFix.rawValue: false,
|
||||||
Keys.messageFilterKeywords.rawValue: []
|
Keys.messageFilterKeywords.rawValue: [],
|
||||||
|
Keys.inputToolbar.rawValue: false
|
||||||
]
|
]
|
||||||
|
|
||||||
@UserDefault(key: Keys.hidePhoneInSettings.rawValue)
|
@UserDefault(key: Keys.hidePhoneInSettings.rawValue)
|
||||||
@ -393,6 +396,9 @@ public class SGSimpleSettings {
|
|||||||
|
|
||||||
@UserDefault(key: Keys.messageFilterKeywords.rawValue)
|
@UserDefault(key: Keys.messageFilterKeywords.rawValue)
|
||||||
public var messageFilterKeywords: [String]
|
public var messageFilterKeywords: [String]
|
||||||
|
|
||||||
|
@UserDefault(key: Keys.inputToolbar.rawValue)
|
||||||
|
public var inputToolbar: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SGSimpleSettings {
|
extension SGSimpleSettings {
|
||||||
|
|||||||
@ -3,15 +3,15 @@ import TextFormat
|
|||||||
import TelegramCore
|
import TelegramCore
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public func chatTextInputAddFormattingAttribute(_ state: ChatTextInputState, attribute: NSAttributedString.Key, value: Any?) -> ChatTextInputState {
|
public func chatTextInputAddFormattingAttribute(forceRemoveAll: Bool = false, _ state: ChatTextInputState, attribute: NSAttributedString.Key, value: Any?) -> ChatTextInputState {
|
||||||
if !state.selectionRange.isEmpty {
|
if !state.selectionRange.isEmpty {
|
||||||
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
let nsRange = NSRange(location: state.selectionRange.lowerBound, length: state.selectionRange.count)
|
||||||
var addAttribute = true
|
var addAttribute = true
|
||||||
var attributesToRemove: [NSAttributedString.Key] = []
|
var attributesToRemove: [NSAttributedString.Key] = []
|
||||||
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
state.inputText.enumerateAttributes(in: nsRange, options: .longestEffectiveRangeNotRequired) { attributes, range, _ in
|
||||||
for (key, _) in attributes {
|
for (key, _) in attributes {
|
||||||
if key == attribute {
|
if key == attribute || forceRemoveAll {
|
||||||
if nsRange == range {
|
if nsRange == range || forceRemoveAll {
|
||||||
addAttribute = false
|
addAttribute = false
|
||||||
attributesToRemove.append(key)
|
attributesToRemove.append(key)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -929,11 +929,20 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
textInputNode.textView.returnKeyType = strongSelf.sendWithReturnKey ? .send : .default
|
textInputNode.textView.returnKeyType = strongSelf.sendWithReturnKey ? .send : .default
|
||||||
textInputNode.textView.reloadInputViews()
|
textInputNode.textView.reloadInputViews()
|
||||||
}
|
}
|
||||||
|
if #available(iOS 13.0, *), let toolbar = strongSelf.toolbarHostingController as? UIHostingController<ChatToolbarView> {
|
||||||
|
toolbar.rootView.setShowNewLine(value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
self.initToolbar()
|
||||||
|
|
||||||
self.addSubnode(self.clippingNode)
|
self.addSubnode(self.clippingNode)
|
||||||
|
|
||||||
|
// MARK: Swiftgram
|
||||||
|
if #available(iOS 13.0, *), let toolbarHostingController = self.toolbarHostingController as? UIHostingController<ChatToolbarView> {
|
||||||
|
self.view.addSubview(toolbarHostingController.view)
|
||||||
|
}
|
||||||
|
|
||||||
self.sendAsAvatarContainerNode.activated = { [weak self] gesture, _ in
|
self.sendAsAvatarContainerNode.activated = { [weak self] gesture, _ in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1212,16 +1221,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
textInputNode.textView.returnKeyType = self.sendWithReturnKey ? .send : .default
|
textInputNode.textView.returnKeyType = self.sendWithReturnKey ? .send : .default
|
||||||
self.textInputNode = textInputNode
|
self.textInputNode = textInputNode
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
if #available(iOS 13.0, *) {
|
|
||||||
let toolbarView = ChatToolbarView()
|
|
||||||
let toolbarHostingController = UIHostingController(rootView: toolbarView)
|
|
||||||
toolbarHostingController.view.frame = CGRect(x: 0, y: 0, width: textInputNode.frame.width, height: 88/*44*/)
|
|
||||||
toolbarHostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
self.toolbarHostingController = toolbarHostingController
|
|
||||||
self.textInputNode?.textView.inputAccessoryView = toolbarHostingController.view
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var accessoryButtonsWidth: CGFloat = 0.0
|
var accessoryButtonsWidth: CGFloat = 0.0
|
||||||
var firstButton = true
|
var firstButton = true
|
||||||
@ -2128,7 +2127,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
if buttonTitleUpdated && !transition.isAnimated {
|
if buttonTitleUpdated && !transition.isAnimated {
|
||||||
transition = .animated(duration: 0.3, curve: .easeInOut)
|
transition = .animated(duration: 0.3, curve: .easeInOut)
|
||||||
}
|
}
|
||||||
|
// MARK: Swiftgram
|
||||||
|
let originalLeftInset = leftInset
|
||||||
var leftInset = leftInset
|
var leftInset = leftInset
|
||||||
|
|
||||||
var textInputBackgroundWidthOffset: CGFloat = 0.0
|
var textInputBackgroundWidthOffset: CGFloat = 0.0
|
||||||
@ -2938,7 +2938,13 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
|
|||||||
self.viewOnceButton.isHidden = true
|
self.viewOnceButton.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return panelHeight
|
// MARK: Swiftgram
|
||||||
|
var toolbarOffset: CGFloat = 0.0
|
||||||
|
if !displayBotStartButton {
|
||||||
|
toolbarOffset = layoutToolbar(transition: transition, panelHeight: panelHeight, width: width, leftInset: originalLeftInset, rightInset: rightInset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return panelHeight + toolbarOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func slowModeButtonPressed() {
|
@objc private func slowModeButtonPressed() {
|
||||||
@ -5057,35 +5063,321 @@ private final class BoostSlowModeButton: HighlightTrackingButtonNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Swiftgram
|
||||||
|
extension ChatTextInputPanelNode {
|
||||||
|
|
||||||
|
func selectLastWordIfIdle() {
|
||||||
|
self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||||
|
// No changes to current selection
|
||||||
|
if !current.selectionRange.isEmpty {
|
||||||
|
return (current, inputMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputText = (current.inputText.mutableCopy() as? NSMutableAttributedString) ?? NSMutableAttributedString()
|
||||||
|
|
||||||
|
// If text is empty, return current state
|
||||||
|
guard inputText.length > 0 else {
|
||||||
|
return (current, inputMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
let plainText = inputText.string
|
||||||
|
let nsString = plainText as NSString
|
||||||
|
|
||||||
|
// Create character set for word boundaries (spaces and newlines)
|
||||||
|
let wordBoundaries = CharacterSet.whitespacesAndNewlines
|
||||||
|
|
||||||
|
// Find last non-whitespace character
|
||||||
|
var endIndex = nsString.length - 1
|
||||||
|
while endIndex >= 0 &&
|
||||||
|
(nsString.substring(with: NSRange(location: endIndex, length: 1)) as NSString)
|
||||||
|
.rangeOfCharacter(from: wordBoundaries).location != NSNotFound {
|
||||||
|
endIndex -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we only had whitespace, return current state
|
||||||
|
guard endIndex >= 0 else {
|
||||||
|
return (current, inputMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find start of the last word by looking backwards for whitespace
|
||||||
|
var startIndex = endIndex
|
||||||
|
while startIndex > 0 {
|
||||||
|
let char = nsString.substring(with: NSRange(location: startIndex - 1, length: 1))
|
||||||
|
if (char as NSString).rangeOfCharacter(from: wordBoundaries).location != NSNotFound {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
startIndex -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create range for the last word
|
||||||
|
let wordLength = endIndex - startIndex + 1
|
||||||
|
let lastWordRange = NSRange(location: startIndex, length: wordLength)
|
||||||
|
|
||||||
|
// Create new selection range
|
||||||
|
let newSelectionRange = lastWordRange.location ..< (lastWordRange.location + lastWordRange.length)
|
||||||
|
|
||||||
|
return (ChatTextInputState(inputText: inputText, selectionRange: newSelectionRange), inputMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initToolbar() {
|
||||||
|
guard #available(iOS 13.0, *) else { return }
|
||||||
|
guard SGSimpleSettings.shared.inputToolbar else { return }
|
||||||
|
guard SGSimpleSettings.shared.b else { return }
|
||||||
|
let toolbarView = ChatToolbarView(
|
||||||
|
onQuote: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesQuote(strongSelf)
|
||||||
|
},
|
||||||
|
onSpoiler: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesSpoiler(strongSelf)
|
||||||
|
},
|
||||||
|
onBold: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesBold(strongSelf)
|
||||||
|
},
|
||||||
|
onItalic: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesItalic(strongSelf)
|
||||||
|
},
|
||||||
|
onMonospace: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesMonospace(strongSelf)
|
||||||
|
},
|
||||||
|
onLink: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesLink(self!)
|
||||||
|
},
|
||||||
|
onStrikethrough: { [weak self]
|
||||||
|
in guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesStrikethrough(strongSelf)
|
||||||
|
},
|
||||||
|
onUnderline: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesUnderline(strongSelf)
|
||||||
|
},
|
||||||
|
onCode: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.selectLastWordIfIdle()
|
||||||
|
strongSelf.formatAttributesCodeBlock(strongSelf)
|
||||||
|
},
|
||||||
|
onNewLine: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
|
||||||
|
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||||
|
let inputText = (current.inputText.mutableCopy() as? NSMutableAttributedString) ?? NSMutableAttributedString()
|
||||||
|
|
||||||
|
// Check if there's selected text
|
||||||
|
let hasSelection = current.selectionRange.count > 0
|
||||||
|
|
||||||
|
if hasSelection {
|
||||||
|
// Move selected text to new line
|
||||||
|
let selectedText = inputText.attributedSubstring(from: NSRange(current.selectionRange))
|
||||||
|
let newLineAttr = NSAttributedString(string: "\n")
|
||||||
|
|
||||||
|
// Insert newline and selected text
|
||||||
|
inputText.replaceCharacters(in: NSRange(current.selectionRange), with: newLineAttr)
|
||||||
|
inputText.insert(selectedText, at: current.selectionRange.lowerBound + 1)
|
||||||
|
|
||||||
|
// Update selection range to end of moved text
|
||||||
|
let newPosition = current.selectionRange.lowerBound + 1 + selectedText.length
|
||||||
|
return (ChatTextInputState(inputText: inputText, selectionRange: newPosition ..< newPosition), inputMode)
|
||||||
|
} else {
|
||||||
|
// Simple newline insertion at current position
|
||||||
|
let attributedString = NSAttributedString(string: "\n")
|
||||||
|
inputText.replaceCharacters(in: NSRange(current.selectionRange), with: attributedString)
|
||||||
|
|
||||||
|
// Update cursor position
|
||||||
|
let newPosition = current.selectionRange.lowerBound + attributedString.length
|
||||||
|
return (ChatTextInputState(inputText: inputText, selectionRange: newPosition ..< newPosition), inputMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// TODO(swiftgram): Binding
|
||||||
|
showNewLine: .constant(true), //.constant(self.sendWithReturnKey)
|
||||||
|
onClearFormatting: { [weak self] in
|
||||||
|
guard let strongSelf = self else { return }
|
||||||
|
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||||
|
return (chatTextInputAddFormattingAttribute(forceRemoveAll: true,current, attribute: ChatTextInputAttributes.allAttributes[0], value: nil), inputMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let toolbarHostingController = UIHostingController(rootView: toolbarView/*, ignoreSafeArea: true*/)
|
||||||
|
self.toolbarHostingController = toolbarHostingController
|
||||||
|
toolbarHostingController.view.backgroundColor = .clear
|
||||||
|
|
||||||
|
// Disable "Swipe to go back" gesture when touching scrollview
|
||||||
|
self.view.interactiveTransitionGestureRecognizerTest = { [weak self] point in
|
||||||
|
if let self, let _ = (self.toolbarHostingController as? UIHostingController<ChatToolbarView>)?.view.hitTest(point, with: nil) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func layoutToolbar(transition: ContainedViewLayoutTransition, panelHeight: CGFloat, width: CGFloat, leftInset: CGFloat, rightInset: CGFloat) -> CGFloat {
|
||||||
|
var toolbarHeight: CGFloat = 0.0
|
||||||
|
var toolbarSpacing: CGFloat = 0.0
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
if let toolbarHostingController = self.toolbarHostingController as? UIHostingController<ChatToolbarView> {
|
||||||
|
toolbarHeight = 44.0
|
||||||
|
toolbarSpacing = 1.0
|
||||||
|
transition.updateFrame(view: toolbarHostingController.view, frame: CGRect(origin: CGPoint(x: leftInset, y: panelHeight + toolbarSpacing), size: CGSize(width: width - rightInset - leftInset, height: toolbarHeight)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toolbarHeight + toolbarSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Swiftgram
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13.0, *)
|
||||||
struct ChatToolbarView: View {
|
struct ChatToolbarView: View {
|
||||||
|
var onQuote: () -> Void
|
||||||
|
var onSpoiler: () -> Void
|
||||||
|
var onBold: () -> Void
|
||||||
|
var onItalic: () -> Void
|
||||||
|
var onMonospace: () -> Void
|
||||||
|
var onLink: () -> Void
|
||||||
|
var onStrikethrough: () -> Void
|
||||||
|
var onUnderline: () -> Void
|
||||||
|
var onCode: () -> Void
|
||||||
|
|
||||||
|
var onNewLine: () -> Void
|
||||||
|
@Binding private var showNewLine: Bool
|
||||||
|
|
||||||
|
var onClearFormatting: () -> Void
|
||||||
|
|
||||||
|
public init(
|
||||||
|
onQuote: @escaping () -> Void,
|
||||||
|
onSpoiler: @escaping () -> Void,
|
||||||
|
onBold: @escaping () -> Void,
|
||||||
|
onItalic: @escaping () -> Void,
|
||||||
|
onMonospace: @escaping () -> Void,
|
||||||
|
onLink: @escaping () -> Void,
|
||||||
|
onStrikethrough: @escaping () -> Void,
|
||||||
|
onUnderline: @escaping () -> Void,
|
||||||
|
onCode: @escaping () -> Void,
|
||||||
|
onNewLine: @escaping () -> Void,
|
||||||
|
showNewLine: Binding<Bool>,
|
||||||
|
onClearFormatting: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
self.onQuote = onQuote
|
||||||
|
self.onSpoiler = onSpoiler
|
||||||
|
self.onBold = onBold
|
||||||
|
self.onItalic = onItalic
|
||||||
|
self.onMonospace = onMonospace
|
||||||
|
self.onLink = onLink
|
||||||
|
self.onStrikethrough = onStrikethrough
|
||||||
|
self.onUnderline = onUnderline
|
||||||
|
self.onCode = onCode
|
||||||
|
self.onNewLine = onNewLine
|
||||||
|
self._showNewLine = showNewLine
|
||||||
|
self.onClearFormatting = onClearFormatting
|
||||||
|
}
|
||||||
|
|
||||||
|
public func setShowNewLine(_ value: Bool) {
|
||||||
|
self.showNewLine = value
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
Button(action: {
|
HStack(spacing: 12) {
|
||||||
// Action 1
|
if showNewLine {
|
||||||
}) {
|
Button(action: onNewLine) {
|
||||||
Image(systemName: "photo")
|
Image(systemName: "return")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
}
|
||||||
|
Button(action: onClearFormatting) {
|
||||||
|
Image(systemName: "pencil.slash")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
Spacer()
|
||||||
|
// Quote Button
|
||||||
|
Button(action: onQuote) {
|
||||||
|
Image(systemName: "text.quote")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Spoiler Button
|
||||||
|
Button(action: onSpoiler) {
|
||||||
|
Image(systemName: "eye.slash")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Bold Button
|
||||||
|
Button(action: onBold) {
|
||||||
|
Image(systemName: "bold")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Italic Button
|
||||||
|
Button(action: onItalic) {
|
||||||
|
Image(systemName: "italic")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Monospace Button
|
||||||
|
Button(action: onMonospace) {
|
||||||
|
if #available(iOS 16.4, *) {
|
||||||
|
Text("M").monospaced()
|
||||||
|
} else {
|
||||||
|
Text("M")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Link Button
|
||||||
|
Button(action: onLink) {
|
||||||
|
Image(systemName: "link")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
// Underline Button
|
||||||
|
Button(action: onUnderline) {
|
||||||
|
Image(systemName: "underline")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
|
||||||
|
// Strikethrough Button
|
||||||
|
Button(action: onStrikethrough) {
|
||||||
|
Image(systemName: "strikethrough")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
|
|
||||||
|
|
||||||
|
// Code Button
|
||||||
|
Button(action: onCode) {
|
||||||
|
Image(systemName: "chevron.left.forwardslash.chevron.right")
|
||||||
|
}
|
||||||
|
.buttonStyle(ToolbarButtonStyle())
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal, 8)
|
||||||
|
.padding(.vertical, 8)
|
||||||
Button(action: {
|
|
||||||
// Action 2
|
|
||||||
}) {
|
|
||||||
Image(systemName: "camera")
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Button(action: {
|
|
||||||
// Action 3
|
|
||||||
}) {
|
|
||||||
Image(systemName: "keyboard")
|
|
||||||
}
|
|
||||||
.padding(.horizontal)
|
|
||||||
}
|
}
|
||||||
.padding(.vertical, 8)
|
.background(Color(UIColor.clear))
|
||||||
// .background(Color(UIColor.systemBackground))
|
}
|
||||||
.background(Color(UIColor.red))
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, *)
|
||||||
|
struct ToolbarButtonStyle: ButtonStyle {
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
configuration.label
|
||||||
|
.font(.system(size: 17))
|
||||||
|
.frame(width: 36, height: 36, alignment: .center)
|
||||||
|
.background(Color(UIColor.tertiarySystemBackground))
|
||||||
|
.cornerRadius(8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user