This discussion is archived
7 Replies Latest reply: Oct 5, 2012 8:00 AM by Kleopatra RSS

Problem with custom composite component and focus

809259 Newbie
Currently Being Moderated
Hello, everybody!

I created a custom composite componente made up of a JFormattedTextField and a JButton inside a custom JPanel . You know that from the point of view of the user, there's only one component, a text field and a button assembled together. He's not aware of the panel underneath. But there's a little conceptual problem for me: when the text field has the focus, the method KeyborardFocusManager#getPermanentFocusOwner returns the text field, of course, but I wouldn't like it to be this way. I would like the JPanel to be returned as the focus owner. Of course I know there's a workaround for this: I have just to create my own utility method getFocusedComponent , for example, test if the focused component is a JFormattedTextField and see if its parent is the custom JPanel . But is there a simpler and elegant way?

Thank you.

Marcos
  • 1. Re: Problem with custom composite component and focus
    jduprez Pro
    Currently Being Moderated
    Hello,

    I may sound unhelpful, but I question whether you have a problem at all :o)
    Here are a few questions to try to uncover whether you really have a problem, and whether the problem is where you think it is.
    I created a custom composite componente made up of a JFormattedTextField and a JButton inside a custom JPanel . You know that from the point of view of the user, there's only one component
    It depends on what you call the user.
    From the point of view of the person using the application with mouse and keyboard (let's call that person the application user ), there really are two components.
    From the POV of the developer using the component as a library, I understand that you want this person to handle only one component.
    when the text field has the focus, the method KeyborardFocusManager#getPermanentFocusOwner returns the text field, of course, but I wouldn't like it to be this way. I would like the JPanel to be returned as the focus owner.
    Why?
    The methods in FocusManager are probably used by the Swing focus mechanism, for which there really are two components. If you start returning a non-default value for this method, you have to make sure that all other focus-related methods behave consistently (e.g. focusNextComponent(...) , focusPreviousComponent(...), up/downFOcusCycle(...),... and so on). Which means writing your own KeyboardFocusManager .
    This means to have a specific focus manager to handle your specific component. This approach doesn't scale if you need to support more than one custom component.

    Maybe someone will come up with a way to do that using the built-in focus manager mechanism and without overriding the focus manager implementation, but if you really have to override it, there's something wrong.
    Of course I know there's a workaround for this: I have just to create my own utility method getFocusedComponent , for example, test if the focused component is a JFormattedTextField and see if its parent is the custom JPanel . But is there a simpler and elegant way?
    I am not advanced at all in using FocusManager methods, but when would you need to call this method KeyborardFocusManager#getPermanentFocusOwner ?
    If this is by some code that knows about the instances of your specific component, maybe you could override your panel's isFocusOwner() instead? Though that might play naughty with the built-in focus mechanism...
    Anyway if we're at the point where the calling code has to know about the instances of the custom component, then your approach is elegant enough.

    Best regards,

    J.
  • 2. Re: Problem with custom composite component and focus
    809259 Newbie
    Currently Being Moderated
    Hello, jduprez. Thank you for the answer.
    jduprez wrote:
    Hello,
    I created a custom composite componente made up of a JFormattedTextField and a JButton inside a custom JPanel . You know that from the point of view of the user, there's only one component
    It depends on what you call the user.
    From the point of view of the person using the application with mouse and keyboard (let's call that person the application user ), there really are two components.
    From the POV of the developer using the component as a library, I understand that you want this person to handle only one component.
    Yes, the user is seeing two components on screen, but even from his point of view he recognizes that these two componentes form just a single component as the text field and the button are next to each other (in fact without any space between them).

    when the text field has the focus, the method KeyborardFocusManager#getPermanentFocusOwner returns the text field, of course, but I wouldn't like it to be this way. I would like the JPanel to be returned as the focus owner.
    Why?
    The methods in FocusManager are probably used by the Swing focus mechanism, for which there really are two components. If you start returning a non-default value for this method, you have to make sure that all other focus-related methods behave consistently (e.g. focusNextComponent(...) , focusPreviousComponent(...), up/downFOcusCycle(...),... and so on). Which means writing your own KeyboardFocusManager .
    This means to have a specific focus manager to handle your specific component. This approach doesn't scale if you need to support more than one custom component.

    Maybe someone will come up with a way to do that using the built-in focus manager mechanism and without overriding the focus manager implementation, but if you really have to override it, there's something wrong.
    I'll give you some reasons.

    Think about this code:
    SearchTextField stf = new SearchTextField(); // this is the custom composite component
    add(stf);
    
    Map<Component, Object> components = new IdentityHashMap<>();
    components.put(stf, new Object());
    
    // Now assume that the text field inside that custom component has the focus
    
    KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    Component focusedComponent = focusManager.getPermanentFocusOwner();
    Object obj = components.get(focusedComponent);
    obj will always be null. This is really non-intuitive. I thought I was dealing with the SearchTextField component, not with its guts. From the point of view of the implementation the focus system is right, I understand that, but from the point of view of the application developer, conceptually it is wrong, because it is forcing me to think low level, about the implementation of the component and not from a higher level of abstraction, that is, the SearchTextField only. For the code above to work I have to expose the implementation details of the component, like this:
    Map<Component, Object> components = new IdentityHashMap<>();
    components.put(stf.getRawTextField(), new Object());
    Another reason is this. Look at the implementation of the util method that I metioned:
    public final class UIUtils
    {
        private UIUtils()
        {
        }
    
        // ...
    
        public static Component getFocusedComponent()
        {
            KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
            Component focusedComponent = focusManager.getPermanentFocusOwner();
    
            if (focusedComponent instanceof JFormattedTextField)
            {
                Component editPanel = focusedComponent.getParent();
                if (editPanel instanceof JPanel && editPanel.getParent() instanceof SearchTextField)
                {
                    focusedComponent = editPanel.getParent();
                }
            }
      
            return focusedComponent;
        }
    
        // ...
    }
    The method getFocusedComponent works well, but the problem with it is that it has to have a deep knowledge of the implementation details of the SearchTextField component. This is clearly a bad thing as now everytime I change the internal structure of the composite component I have to update this util method. This bad smell even has a name: Shotgun Surgery, according the the book Refactoring, by Martin Fowler . I don't need to be changing another class when one class change. What if I forget to change the other class?

    I really think Swing could have a way to make what I want possible without contradicting the focus system. It had to have considered scenarios like this. By the way, If I'm not wrong, Delphi has a way to do this, so I'm sure this is not a non-sense thing.

    Marcos
  • 3. Re: Problem with custom composite component and focus
    800268 Expert
    Currently Being Moderated
    Why do you need to do this anyway?

    But you're look up method could just look like this:
    KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    Component focusedComponent = focusManager.getPermanentFocusOwner();
    // easy case (assuming don't allow null objects)
    Object object = components.get(focusedComponent);
    if(object != null) {
      return object;
    }
    // compound components
    for(Entry<Component, Object> entry : components.entrySet()) {
      if(SwingUtilities.isDescendingFrom(focusedComponent, entry.getKey()) {
        return entry.getValue();
      }
    }
    return null;
  • 4. Re: Problem with custom composite component and focus
    809259 Newbie
    Currently Being Moderated
    Walter Laan wrote:
    Why do you need to do this anyway?
    I thought I had already made my point clear, but I'll try to explain once again. When you create a component, you expect your component to be the main thing, the king, the protagonist. In the case of a composite component, the internal components used are just an implementation detail, so they should not leak to the application developer unless if explicitly asked for. But the Swing focus subsystem reveals them. In other words, there should be a way to say: "Swing, this is my custom component. Everytime the +KeyboardFocusManager#getPermanentFocusOwner+ method is called and one of the internal components has the focus I want you to return the main component, and everytime the +JComponent#requestFocusInWindow+ method is called on my custom component I want you to focus this particular internal component of it."

    Ok, I've already realized that there's no such a feature in Swing, so I'll live with my humble util method workaround.

    PS.: I've just remembered that JComboBox is a composite component. Maybe its designers had the problem I'm having and found a solution. I'll see later if it behaves as expected.

    Marcos
  • 5. Re: Problem with custom composite component and focus
    800268 Expert
    Currently Being Moderated
    marcos_aps wrote:
    Walter Laan wrote:
    Why do you need to do this anyway?
    I thought I had already made my point clear, but I'll try to explain once again.
    That is what you want to happen, not why. As in why do you need to get some object mapped to the focus owner in the first place?
    As for requestFocusInWindow, you can delegate that to the textfield or the button for you compound component.

    And no, an editable combo box is not focused when its editor is focus (the drop down button typically isn't focusable).
  • 6. Re: Problem with custom composite component and focus
    809259 Newbie
    Currently Being Moderated
    Walter Laan wrote:
    marcos_aps wrote:
    Walter Laan wrote:
    Why do you need to do this anyway?
    I thought I had already made my point clear, but I'll try to explain once again.
    That is what you want to happen, not why. As in why do you need to get some object mapped to the focus owner in the first place?
    Yes. I realized that another alternative to using the util method I created is to create a custom KeyboardFocusManager derived from DefaultKeyboardFocusManager and override the method getPermanentFocusOwner to deal with my composite component. I can do the mapping there.
    As for requestFocusInWindow, you can delegate that to the textfield or the button for you compound component.
    Right. I'll override this method.
    And no, an editable combo box is not focused when its editor is focus (the drop down button typically isn't focusable).
    Yes, I also confirmed this with a test.

    Marcos
  • 7. Re: Problem with custom composite component and focus
    Kleopatra Journeyer
    Currently Being Moderated
    focus is a very low-level property - it's meant to reveal the really real single owner, however deep in the hierarchy it might be hidden (from a higher perspective). As you noticed, there is no direct support for something like parent-of-focused-component, so you are on your own (if you really need it, which I'm not fully convinced but that's just me:)

    And no: don't fiddle with the KeyboardManager without an extremely valid reason (which you don't have, there are less risky means) - focus is tricky as-is, installing a custom manager that breaks its contract (by not returning the real owner, but something higher up) only adds to the pain.

    One option to achieve it, is to decorate the compound with a JLayer (in jdk7, or JXLayer in jdk6) and implement its ui to listen to focus changes of children.

    Cheers
    Jeanette

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points