danielsaidi / RichTextKit

RichTextKit is a Swift SDK that helps you use rich text in Swift and SwiftUI.
MIT License
814 stars 97 forks source link

FocusedContext is nil while cursor is active in a RichTextField - MacOS #161

Closed S1D1T1 closed 3 months ago

S1D1T1 commented 3 months ago

Summary: @ FocusedValue(.richTextContext) incorrectly returns nil during a button action initiated via that button’s keyboard shortcut.

My MacOS app applies an operation to the currently active RichTextEditor. This operation is a button action.

The action operates correctly on the currently focused text - when clicking on the button. However if initiated via the button’s keyboard shortcut, FocusedValue(.richTextContext) evaluates to nil.

This functioned correctly in the past, though I have incomplete information when it stopped working, or whether there may have been a change in my usage.

Sample code.


…
  @FocusedValue(\.richTextContext) var focusedTextContext:RichTextContext?
…
    Button(
      action: {
        if let focusedTextContext {      // keyboard shortcut gets here, but bails out on nil value
          let processor = PromptProcessor(context: focusedTextContext)    // clicking button gets me here
          processor.promote()
        }
      },
      label:{
        Image(systemName: "chevron.up.square")
      }
)
    .keyboardShortcut(.upArrow)  // This is command-upArrow, since all button shortcuts have implicit .command modifier

(note, I tried different keyboard shortcuts to no avail, it’s not apparently due to special handling of Cmd-Up)

S1D1T1 commented 3 months ago

incorrectly posted to one of your other repos, sorry for the spam. my first ever submitting a a GitHub issue. can you tell?

danielsaidi commented 3 months ago

Hi @S1D1T1

I just tested the menu commands in the macOS app, and they seem to work while using the focused values.

How are you using it? Could it be that a button within the view hierarchy will steal the focus from the window in a way that makes the focused context become nil?

S1D1T1 commented 3 months ago

thanks for checking on it. This action is only initiated by button (real button, not a menu "button"), it's not a menu command. And the behavior occurs only when using the button's key equivalent. When I mouseclick on the button, the action fires, and focused context is valid.

Could it be that a button within the view hierarchy will steal the focus from the window.

this is my current theory. If it does steal focus - in order to catch that keystroke, then it does so very briefly, just for the duration of the button action & then restores it. There's no visible change of focus.

The button is in the .toolbar() of that window. I rely on the focused context because the window has 2 RichtextEditors, so the action needs to know which field to act on. I can put together some code for a test case if you like.

Can you suggest any diagnostic code to add, which would log that moment when the focused context in nil'ed

S1D1T1 commented 3 months ago

https://github.com/danielsaidi/RichTextKit/assets/156350598/d0c29091-cbe9-462b-9990-b23288418e07

screen recording demonstrates. See the above code for the button action called in this demo. When clicking on the buttons in the toolbar, the button action is able to access the focused context, and correctly applies the text transform to the selection in the currently active RichTextEditor.

The demo then illustrates the buttons triggered via key shortcut - watch closely and you can see the buttons animate. this case fails with a nil focused context. this is the far more common & convenient method to use this feature

danielsaidi commented 3 months ago

I started looking at using @FocusedObject instead, in case that would improve things, but it requires iOS 16. I think that you should probably use the context instead. All the other components do so.

S1D1T1 commented 3 months ago

Thanks for the suggestion. I changed my code to use @FocusedObject: I added a .focusedObject() modifier to my RichTextEditor, and declared a @FocusedObject variable to my button action ... and got the same behavior. focused object is valid during button clicks, but nil in key shortcuts. As mentioned I know this previously worked, perhaps the behavior change is in swiftui's button handling. At this point I may resort to my own focus tracking - this particular function relies far more heavily on the keyboard shortcut during typing, than on mouse clicks.

danielsaidi commented 3 months ago

Thank you for investigating, hope that you get it to work with your own approach!

S1D1T1 commented 2 months ago

Solved: isEditingText is valid!

    // MARK: - Bindable Properies

    /// Whether or not the text is currently being edited.
    @Published
    public var isEditingText = false
danielsaidi commented 2 months ago

@S1D1T1 Sounds great, can you please explain a bit? :)

S1D1T1 commented 2 months ago

Sure. My app window has multiple richTextEditors. in my original example, I accessed @FocusedValue(\.richTextContext), to know which text field to act on, during a button action. But the button action failed because richContext is nil for some reason - just in cases where a button is triggered by its key equivalent.

I found an alternate way to determine the current RTE - by checking the isEditingText property which you provided for the RichTextContext class. This flag is true for the currently active richTextContext. And importantly, button key equivalents don't mess it up!

my solution doesn't as cleanly return the current context, it must manually check each RTE to find which has the isEditingText flag set, but there are only 2 fields (a positive and negative image prompt), so its quite manageable.

  func currentContext() -> RichTextContext?{
    if myStyledPrompt.posContext.isEditingText {
      return myStyledPrompt.posContext
    }
    else if myStyledPrompt.negContext.isEditingText {
      return myStyledPrompt.negContext
    }
    return nil
  }
danielsaidi commented 2 months ago

That's perfect 👍