Input fields and buttons are examples of focusable widgets, or “focusables”. Focusables process keyboard events when they have keyboard input focus. There are two general ways for a focusable to gain input focus: by clicking on it with the primary pointer button, or by using the Tab and Shift-Tab keys, which cycle through the focusable fields in the window.
A dashed border gets typically drawn around the widget that has keyboard input focus. Tab moves the keyboard input focus to the next widget, and Shift-Tab moves the keyboard input focus to the previous widget.
    The tabbing order does not get determined by the widgets'
    position, but rather their creation order. Focusables
    inherit from
    x::w::focusable
    objects, which implement several methods:
  
/* ** Copyright 2017-2021 Double Precision, Inc. ** See COPYING for distribution information. */ #include "config.h" #include "close_flag.H" #include <x/exception.H> #include <x/destroy_callback.H> #include <x/weakcapture.H> #include <x/appid.H> #include <x/w/main_window.H> #include <x/w/gridlayoutmanager.H> #include <x/w/gridfactory.H> #include <x/w/button.H> #include <x/w/focusable.H> #include <x/visitor.H> #include <string> #include <iostream> std::string x::appid() noexcept { return "focusable.examples.w.libcxx.com"; } void create_mainwindow(const x::w::main_window &main_window) { auto layout=main_window->gridlayout(); layout->col_alignment(0, x::w::halign::center); auto button1=layout->append_row() ->create_button("Button 1"); auto button2=layout->append_row() ->create_button("Button 2: disable button 1"); auto button3=layout->append_row() ->create_button("Button 3: enable button 1"); auto button4=layout->append_row() ->create_button("Button 4: button 2 gets focus before button 1"); auto button5=layout->append_row() ->create_button("Button 5: button 3 gets focus after button 2"); auto button6=layout->append_row() ->create_button("Button 6: button 1 gets focus first"); auto button7=layout->append_row() ->create_button("Button 7: move focus to button 1"); // Note - normally a callback cannot capture reference to its parent // (or children) display elements, because this would create an // internal circular reference. // // In all of the following cases, a callback for a given button captures // a reference to another button, which is neither its parent or child. button2->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button1->set_enabled(false); }); button3->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button1->set_enabled(true); }); button4->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button2->get_focus_before(button1); }); button5->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button3->get_focus_after(button2); }); button6->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button1->get_focus_first(); }); button6->on_keyboard_focus([] (ONLY IN_THREAD, x::w::focus_change f, const x::w::callback_trigger_t &t) { std::cout << "Button 6 in focus: " << x::w::in_focus(f) << " due to " << t.index() << std::endl; }); button6->on_key_event ([] (ONLY IN_THREAD, const x::w::all_key_events_t &ke, bool activated, const x::w::busy &mcguffin) { // We get both key press and release events. The // "activated" flag indicates whether the library // would normally act on this key event, based on // whether it is a press or a release. // // When a display element responds to a particular // key, it should do so only when "activated" is set. // // If the display element responds to a particular // key, the callback should return true whether or not // activated is set (but take no action if activated // is not set). If the display element does not // recognize the key combination, the display element // should return false. // // The activated flag always gets set for pasted // unicode text from the X input method manager. if (activated) std::cout << "activated: "; std::visit(x::visitor{ [](const x::w::key_event *keptr) { std::cout << (keptr->keypress ? "press ":"release "); if (keptr->unicode) std::cout << "U+" << (uint32_t)keptr->unicode; else std::cout << "keysym " << keptr->keysym; std::cout << std::endl; }, [](const std::u32string_view *keptr) { // Buttons don't receive pasted unicode text // from the X input method manager. // this is for demo purposes. }, [](const x::w::all_key_events_is_not_copyable &) { // Stub to enforce all_key_events_t // untouchability. }}, ke); // This callback takes no action on anything, it just // dumps its parameters, so we always return false. return false; }); button7->on_activate([=] (ONLY IN_THREAD, const x::w::callback_trigger_t &trigger, const x::w::busy &mcguffin) { button1->request_focus(); }); } void focusables() { x::destroy_callback::base::guard guard; auto close_flag=close_flag_ref::create(); auto main_window=x::w::main_window ::create(create_mainwindow, x::w::new_gridlayoutmanager{}); main_window->on_disconnect([] { _exit(1); }); guard(main_window->connection_mcguffin()); main_window->set_window_title("Focusable fields"); main_window->on_delete ([close_flag] (ONLY IN_THREAD, const x::w::busy &ignore) { close_flag->close(); }); main_window->show_all(); close_flag->wait(); } int main(int argc, char **argv) { try { focusables(); } catch (const x::exception &e) { e->caught(); exit(1); } return 0; }
    get_focus_after(),
    get_focus_before(), and
    get_focus_first() moves the focusable widget's
    tabbing order to be after another focusable widget, before another
    focusable widget, or the first focusable widget in the window after
    the window's menu, if it has one; or the first
    focusable widget in windows without menu bars.
  
    set_enabled() enables or disables a focusable
    widget. Disabled focusable widgets do not respond to
    pointer clicks, and tabbing the input focus skips them. Disabled
    focusable widgets get drawn with a dithered mask that blends
    them with the background color, making them appear faint compared to
    enabled widgets.
  
    request_focus() explicitly moves the keyboard
    input focus to the given focusable widget.
  
A widgets that are disabled or not visible cannot receive keyboard focus. In that case the keyboard focus gets moved whenever the widget can receive keyboard focus, unless a different widget requests keyboard focus first.
      request_focus() takes an optional parameter.
      A true value results in the keyboard focus
      moving to the focusable widget if it's eligible to receive keyboard
      focus at this time, otherwise this gets ignored and no delayed
      keyboard focus movement takes place.
    
The default behavior makes it possible to create a new widget, make it visible and immediately set the keyboard focus to the new widget. It takes some time for the connection thread to prepare the new widget and draw it; and the default behavior produces the expected results.
    on_keyboard_focus() and
    on_key_event() install callbacks that
    provide keyboard focus event feedback.
  
    The on_keyboard_focus() callback gets invoked
    whenever the focusable widget gains or loses keyboard input
    focus. Additionally, the callback gets invoked upon installation to
    report the focusable widget's current focus (which is typically
    no input focus for newly-created widgets).
    The on_key_event() callback gets invoked
    when a key gets pressed or released, or when a unicode string gets
    composed using the X Input Method server, while the focusable widget receives input focus.
  
    The on_key_event() callback must return
    true if the callback recognized and processed the
    key event. Returning false results in LibCXX Widget Toolkit's default
    action for the key event; but LibCXX Widget Toolkit calls the
    on_key_event() callback only when the key event
    results in no specific action by the focusable widget.
  
    focusable.C installs an
    on_key_event() callback into one of the input
    buttons. This callback does not get called for
    Enter and Space keys, but for all
    other keys (that the X display server does not handle itself). This is
    because Enter and Space keys have the
    same effect as clicking the button with the pointer, and this specific
    action takes precedence.