Focusable UI components

Every Zebra UI component can be made focusable. A focusable component becomes a participant of focus management what means the component can gain focus and handle key events. Only one Zebra UI components can hold focus at the certain period of time. Zebra focus traversing is done by “zebra.ui.FocusManager” that implements an algorithm of the traversing, triggers focus lost and gained events and provides reference to the current focus owner component. The focus manager is accessible as “zebra.ui.focusManager” variable:

...
// get an UI component that currently holds the focus
zebra.ui.focusManager.focusOwner
... 

To make a component focusable you have to either set “canHaveFocus” property to “true” or implement “canHaveFocus()” method that returns boolean value. Zebra focus manager tests the variable or calls the method to ask if the given component wants to catch focus. If the answer is “true”, the component gets focus and can listen key events have occurred in system:

// instantiate "zebra.ui.Panel" anonymous class that can catches focus
var focusablePan = new zebra.ui.Panel([
     // implement method that makes the component focusable
     function canHaveFocus() { return true; },

     // the component is focusable so it can handle key events. 
     // Let's define key pressed events handler
     function keyPressed(e) {
        ...
     }
]);

Zebra UI component provides few focus related handy methods listed in the table below:

UI Component focus related API Description
hasFocus() tests if the UI component is focus owner
requestFocus() forces Zebra focus manager to pass the focus to the UI component
requestFocusIn(milliseconds) forces Zebra focus manager to pass the focus to the UI component in specified number of milliseconds
focused() the method is called whenever an UI component focus state (focus gained or focus lost) is updated

Focus events

Component focus event handling

To listen focus gained and focus lost events that take place for the given Zebra UI component developers have to implement appropriate focus event handler methods: “focusLost(e)” or “focusGained(e)”

For instance:

// button is focusable component, let's listen when a button 
// gains and looses focus and change button label color depending on
// its focus state
var b = new zebra.ui.Button("Button", [
    // implement focus gained event handler
    function focusGained(e){
        this.focusComponent.setColor("orange"); // set button label text color
    },

    // implement focus lost event handler
    function focusLost(e){
        this.focusComponent.setColor("black"); // set button label text color
    }
]);

Another way to be informed about updating an UI component focus state is overriding the component class “focused()” method. The method is designed to be called every time a component focus state is updated. Developers have to call super method when they override “focused()” method. Let’s re-do the same example demonstrated above but using “focused()” method overriding:

// button is focusable component, let's listen when a button 
// gains and looses focus and change button label color appropriately 
var button = new zebra.ui.Button("Button", [                                             
    // override "focused()" method that is called every time the component 
    // focus state is updated 
    function focused(){
        // don't forget to call super
        this.$super();
 
        // test if the component has focus
        if (this.hasFocus()) {
           // set label text color
           this.focusComponent.setColor("orange"); 
        }
        else {
           // set label text color
           this.focusComponent.setColor("black"); 
        }
    }
]);

Global focus event handling

Zebra event manager allows developers to catch all focus gained and lost events generated for all UI components. Do it as follow:

// register global focus events listener 
// The listener changes button labels color depending on button component 
// focus state. The listener has global effect to all buttons components 
zebra.ui.events.addListener({
    // implement focus gained event handler
    focusGained : function (e){
        if (e.source.focusComponent) {
            e.source.focusComponent.setColor("orange"); // set button label text color
        }
    },

    // implement focus lost event handler
    focusLost : function(e){
        if (e.source.focusComponent) {
            e.source.focusComponent.setColor("black"); // set button label text color
        }
    }    
});

Visualize focus possession

The most evident way to visualize an UI component has focus is implementing “paint(g)” or “paintOnTop(g)” methods. Imagine we would like to indicate the component is focus owner with drawing a border around it:

var p = new zebra.ui.Panel([
   // say the component is focusable
   function canHaveFocus() { return true; },
   
   // override "focused()" method to initiate component 
   // repainting whenever focus state is updated 
   function focused() {
       this.$super();
       this.repaint();
   },

   // draw red border when the component has focus
   function paintOnTop(g) {
      if (this.hasFocus()) {
          g.lineWidth = 2;
          g.setColor("red");
          g.rect(2,2,this.width-4,this.height-4);
          g.stroke();
      }
   }
]);

Zebra has more graceful way to visualize a component focus state. You can make an UI component border view dependent from the component focus state:

    var p = new zebra.ui.Panel([
       // say the component is focusable
       function canHaveFocus() { return true; }
    ]);

    // set view set that defines one view to be used to   
    // visualize the component gains the focus and another one 
    // to visualize the component does not gain the focus.  
    // set the view set as the component border view 
    p.setBorder(new zebra.ui.ViewSet({
        focuson : new zebra.ui.Border("red", 2),
        focusoff: new zebra.ui.Border("gray", 2)
    }));